VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnDDataObject.cpp@ 49966

Last change on this file since 49966 was 49947, checked in by vboxsync, 11 years ago

Additions/WINNT: fix OSE

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.6 KB
Line 
1/* $Id: VBoxDnDDataObject.cpp 49947 2013-12-17 08:40:37Z vboxsync $ */
2/** @file
3 * VBoxDnDDataObject.cpp - IDataObject implementation.
4 */
5
6/*
7 * Copyright (C) 2013 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#include <windows.h>
18#include <new> /* For bad_alloc. */
19#include <shlobj.h>
20
21#include <iprt/path.h>
22#include <iprt/semaphore.h>
23#include <iprt/uri.h>
24
25#include "VBoxTray.h"
26#include "VBoxHelpers.h"
27#include "VBoxDnD.h"
28
29/** @todo Implement IDataObjectAsyncCapability interface? */
30
31VBoxDnDDataObject::VBoxDnDDataObject(FORMATETC *pFormatEtc,
32 STGMEDIUM *pStgMed, ULONG cFormats)
33 : mStatus(Uninitialized),
34 mRefCount(1),
35 mcFormats(0),
36 mpvData(NULL),
37 mcbData(0)
38{
39 HRESULT hr;
40
41 /* Make sure that there's enough room for our fixed formats. */
42 ULONG cAllFormats = cFormats + 1;
43
44 try
45 {
46 mpFormatEtc = new FORMATETC[cAllFormats];
47 RT_BZERO(mpFormatEtc, sizeof(FORMATETC) * cAllFormats);
48 mpStgMedium = new STGMEDIUM[cAllFormats];
49 RT_BZERO(mpStgMedium, sizeof(STGMEDIUM) * cAllFormats);
50
51 if ( pFormatEtc
52 && pStgMed)
53 {
54 for (ULONG i = 0; i < cFormats; i++)
55 {
56 LogFlowFunc(("Format %RU32: cfFormat=%RI16, tyMed=%RU32, dwAspect=%RU32\n",
57 i, pFormatEtc[i].cfFormat, pFormatEtc[i].tymed, pFormatEtc[i].dwAspect));
58 mpFormatEtc[i] = pFormatEtc[i];
59 mpStgMedium[i] = pStgMed[i];
60 }
61 }
62
63 hr = S_OK;
64 }
65 catch (std::bad_alloc &)
66 {
67 hr = E_OUTOFMEMORY;
68 }
69
70 if (SUCCEEDED(hr))
71 {
72 int rc2 = RTSemEventCreate(&mSemEvent);
73 AssertRC(rc2);
74
75 /* Most commonly used format. */
76 RegisterFormat(&mpFormatEtc[cFormats], CF_HDROP);
77 mpStgMedium[cFormats++].tymed = TYMED_HGLOBAL;
78#if 0
79 /* IStream. */
80 RegisterFormat(&mpFormatEtc[cFormats++],
81 RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR));
82 RegisterFormat(&mpFormatEtc[cFormats++],
83 RegisterClipboardFormat(CFSTR_FILECONTENTS),
84 TYMED_ISTREAM, 0 /* lIndex */);
85
86 /* Required for e.g. Windows Media Player. */
87 RegisterFormat(&mpFormatEtc[cFormats++],
88 RegisterClipboardFormat(CFSTR_FILENAME));
89 RegisterFormat(&mpFormatEtc[cFormats++],
90 RegisterClipboardFormat(CFSTR_FILENAMEW));
91 RegisterFormat(&mpFormatEtc[cFormats++],
92 RegisterClipboardFormat(CFSTR_SHELLIDLIST));
93 RegisterFormat(&mpFormatEtc[cFormats++],
94 RegisterClipboardFormat(CFSTR_SHELLIDLISTOFFSET));
95#endif
96 mcFormats = cFormats;
97 mStatus = Initialized;
98 }
99
100 LogFlowFunc(("cFormats=%RU32, hr=%Rhrc\n", cFormats, hr));
101}
102
103VBoxDnDDataObject::~VBoxDnDDataObject(void)
104{
105 if (mpFormatEtc)
106 delete[] mpFormatEtc;
107
108 if (mpStgMedium)
109 delete[] mpStgMedium;
110
111 if (mpvData)
112 RTMemFree(mpvData);
113
114 LogFlowFunc(("mRefCount=%RI32\n", mRefCount));
115}
116
117/* static */
118int VBoxDnDDataObject::CreateDataObject(FORMATETC *pFormatEtc, STGMEDIUM *pStgMeds,
119 ULONG cFormats, IDataObject **ppDataObject)
120{
121 AssertPtrReturn(pFormatEtc, VERR_INVALID_POINTER);
122 AssertPtrReturn(pStgMeds, VERR_INVALID_POINTER);
123 AssertPtrReturn(ppDataObject, VERR_INVALID_POINTER);
124
125 int rc;
126 try
127 {
128 *ppDataObject = new VBoxDnDDataObject(pFormatEtc, pStgMeds, cFormats);
129 rc = VINF_SUCCESS;
130 }
131 catch(std::bad_alloc &)
132 {
133 rc = VERR_NO_MEMORY;
134 }
135
136 return rc;
137}
138
139/*
140 * IUnknown methods.
141 */
142
143STDMETHODIMP_(ULONG) VBoxDnDDataObject::AddRef(void)
144{
145 return InterlockedIncrement(&mRefCount);
146}
147
148STDMETHODIMP_(ULONG) VBoxDnDDataObject::Release(void)
149{
150 LONG lCount = InterlockedDecrement(&mRefCount);
151 if (lCount == 0)
152 {
153 delete this;
154 return 0;
155 }
156
157 return lCount;
158}
159
160STDMETHODIMP VBoxDnDDataObject::QueryInterface(REFIID iid, void **ppvObject)
161{
162 AssertPtrReturn(ppvObject, E_INVALIDARG);
163
164 if ( iid == IID_IDataObject
165 || iid == IID_IUnknown)
166 {
167 AddRef();
168 *ppvObject = this;
169 return S_OK;
170 }
171
172 *ppvObject = 0;
173 return E_NOINTERFACE;
174}
175
176/**
177 * Retrieves the data stored in this object and store the result in
178 * pMedium.
179 *
180 * @return IPRT status code.
181 * @return HRESULT
182 * @param pFormatEtc
183 * @param pMedium
184 */
185STDMETHODIMP VBoxDnDDataObject::GetData(FORMATETC *pFormatEtc, STGMEDIUM *pMedium)
186{
187 AssertPtrReturn(pFormatEtc, DV_E_FORMATETC);
188 AssertPtrReturn(pMedium, DV_E_FORMATETC);
189
190 LogFlowFunc(("pFormatEtc=%p, pMedium=%p\n", pFormatEtc, pMedium));
191
192 ULONG lIndex;
193 if (!LookupFormatEtc(pFormatEtc, &lIndex)) /* Format supported? */
194 return DV_E_FORMATETC;
195 if (lIndex >= mcFormats) /* Paranoia. */
196 return DV_E_FORMATETC;
197
198 FORMATETC *pThisFormat = &mpFormatEtc[lIndex];
199 AssertPtr(pThisFormat);
200
201 STGMEDIUM *pThisMedium = &mpStgMedium[lIndex];
202 AssertPtr(pThisMedium);
203
204 HRESULT hr = DV_E_FORMATETC;
205
206 LogFlowFunc(("mStatus=%ld\n", mStatus));
207 if (mStatus == Dropping)
208 {
209 LogFlowFunc(("Waiting for event ...\n"));
210 int rc2 = RTSemEventWait(mSemEvent, RT_INDEFINITE_WAIT);
211 LogFlowFunc(("rc=%Rrc, mStatus=%ld\n", rc2, mStatus));
212 }
213
214 if (mStatus == Dropped)
215 {
216 LogFlowFunc(("cfFormat=%RI16, sFormat=%s, tyMed=%RU32, dwAspect=%RU32\n",
217 pThisFormat->cfFormat, VBoxDnDDataObject::ClipboardFormatToString(pFormatEtc->cfFormat),
218 pThisFormat->tymed, pThisFormat->dwAspect));
219 LogFlowFunc(("Got strFormat=%s, pvData=%p, cbData=%RU32\n",
220 mstrFormat.c_str(), mpvData, mcbData));
221
222 if (mstrFormat.equalsIgnoreCase("text/uri-list"))
223 {
224 RTCList<RTCString> lstFilesURI = RTCString((char*)mpvData, mcbData).split("\r\n");
225 RTCList<RTCString> lstFiles;
226 for (size_t i = 0; i < lstFilesURI.size(); i++)
227 {
228 /* Extract path from URI. */
229 char *pszPath = RTUriPath(lstFilesURI.at(i).c_str());
230 if ( pszPath
231 && strlen(pszPath) > 1)
232 {
233 pszPath++; /** @todo Skip first '/' (part of URI). Correct? */
234 pszPath = RTPathChangeToDosSlashes(pszPath, false /* fForce */);
235 lstFiles.append(pszPath);
236 }
237 }
238#ifdef DEBUG
239 LogFlowFunc(("Files (%zu)\n", lstFiles.size()));
240 for (size_t i = 0; i < lstFiles.size(); i++)
241 LogFlowFunc(("\tFile: %s\n", lstFiles.at(i).c_str()));
242#endif
243
244#if 0
245 if ( (pFormatEtc->tymed & TYMED_ISTREAM)
246 && (pFormatEtc->dwAspect == DVASPECT_CONTENT)
247 && (pFormatEtc->cfFormat == CF_FILECONTENTS))
248 {
249
250 }
251 else if ( (pFormatEtc->tymed & TYMED_HGLOBAL)
252 && (pFormatEtc->dwAspect == DVASPECT_CONTENT)
253 && (pFormatEtc->cfFormat == CF_FILEDESCRIPTOR))
254 {
255
256 }
257 else if ( (pFormatEtc->tymed & TYMED_HGLOBAL)
258 && (pFormatEtc->cfFormat == CF_PREFERREDDROPEFFECT))
259 {
260 HGLOBAL hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE | GMEM_ZEROINIT, sizeof(DWORD));
261 DWORD *pdwEffect = (DWORD *)GlobalLock(hData);
262 AssertPtr(pdwEffect);
263 *pdwEffect = DROPEFFECT_COPY;
264 GlobalUnlock(hData);
265
266 pMedium->hGlobal = hData;
267 pMedium->tymed = TYMED_HGLOBAL;
268 }
269 else
270#endif
271 if ( (pFormatEtc->tymed & TYMED_HGLOBAL)
272 && (pFormatEtc->dwAspect == DVASPECT_CONTENT)
273 && (pFormatEtc->cfFormat == CF_TEXT))
274 {
275 pMedium->hGlobal = GlobalAlloc(GHND, mcbData + 1);
276 if (pMedium->hGlobal)
277 {
278 /** @todo Not working yet -- needs URI to plain ASCII conversion. */
279
280 char *pcDst = (char *)GlobalLock(pMedium->hGlobal);
281 memcpy(pcDst, mpvData, mcbData);
282 pcDst[mcbData] = '\0';
283 GlobalUnlock(pMedium->hGlobal);
284
285 hr = S_OK;
286 }
287 }
288 else if ( (pFormatEtc->tymed & TYMED_HGLOBAL)
289 && (pFormatEtc->dwAspect == DVASPECT_CONTENT)
290 && (pFormatEtc->cfFormat == CF_HDROP))
291 {
292 int rc = VINF_SUCCESS;
293
294 size_t cchFiles = 0; /* Number of ASCII characters. */
295 for (size_t i = 0; i < lstFiles.size(); i++)
296 {
297 cchFiles += strlen(lstFiles.at(i).c_str());
298 cchFiles += 1; /* Terminating '\0'. */
299 }
300
301 size_t cbBuf = sizeof(DROPFILES) + ((cchFiles + 1) * sizeof(RTUTF16));
302 DROPFILES *pBuf = (DROPFILES *)RTMemAllocZ(cbBuf);
303 if (pBuf)
304 {
305 pBuf->pFiles = sizeof(DROPFILES);
306 pBuf->fWide = 1; /* We use unicode. Always. */
307
308 uint8_t *pCurFile = (uint8_t *)pBuf + pBuf->pFiles;
309 AssertPtr(pCurFile);
310
311 for (size_t i = 0; i < lstFiles.size() && RT_SUCCESS(rc); i++)
312 {
313 size_t cchCurFile;
314 PRTUTF16 pwszFile;
315 rc = RTStrToUtf16(lstFiles.at(i).c_str(), &pwszFile);
316 if (RT_SUCCESS(rc))
317 {
318 cchCurFile = RTUtf16Len(pwszFile);
319 Assert(cchCurFile);
320 memcpy(pCurFile, pwszFile, cchCurFile * sizeof(RTUTF16));
321 RTUtf16Free(pwszFile);
322 }
323 else
324 break;
325
326 pCurFile += cchCurFile * sizeof(RTUTF16);
327
328 /* Terminate current file name. */
329 *pCurFile = L'\0';
330 pCurFile += sizeof(RTUTF16);
331 }
332
333 if (RT_SUCCESS(rc))
334 {
335 *pCurFile = L'\0'; /* Final list terminator. */
336
337 pMedium->tymed = TYMED_HGLOBAL;
338 pMedium->pUnkForRelease = NULL;
339 pMedium->hGlobal = GlobalAlloc( GMEM_ZEROINIT
340 | GMEM_MOVEABLE
341 | GMEM_DDESHARE, cbBuf);
342 if (pMedium->hGlobal)
343 {
344 LPVOID pMem = GlobalLock(pMedium->hGlobal);
345 if (pMem)
346 {
347 memcpy(pMem, pBuf, cbBuf);
348 GlobalUnlock(pMedium->hGlobal);
349
350 hr = S_OK;
351 }
352 }
353 }
354
355 RTMemFree(pBuf);
356 }
357 }
358 }
359 }
360
361 if (FAILED(hr))
362 {
363 LogFlowFunc(("Copying medium ...\n"));
364 switch(pThisMedium->tymed)
365 {
366
367 case TYMED_HGLOBAL:
368 pMedium->hGlobal = (HGLOBAL)OleDuplicateData(pThisMedium->hGlobal, pThisFormat->cfFormat, NULL);
369 break;
370
371 default:
372 break;
373 }
374
375 pMedium->tymed = pThisFormat->tymed;
376 pMedium->pUnkForRelease = NULL;
377 }
378
379 LogFlowFunc(("hr=%Rhrc\n", hr));
380 return hr;
381}
382
383/**
384 * Only required for IStream / IStorage interfaces.
385 *
386 * @return IPRT status code.
387 * @return HRESULT
388 * @param pFormatEtc
389 * @param pMedium
390 */
391STDMETHODIMP VBoxDnDDataObject::GetDataHere(FORMATETC *pFormatEtc, STGMEDIUM *pMedium)
392{
393 LogFlowFunc(("\n"));
394 return DATA_E_FORMATETC;
395}
396
397/**
398 * Query if this objects supports a specific format.
399 *
400 * @return IPRT status code.
401 * @return HRESULT
402 * @param pFormatEtc
403 */
404STDMETHODIMP VBoxDnDDataObject::QueryGetData(FORMATETC *pFormatEtc)
405{
406 LogFlowFunc(("\n"));
407 return (LookupFormatEtc(pFormatEtc, NULL /* puIndex */)) ? S_OK : DV_E_FORMATETC;
408}
409
410STDMETHODIMP VBoxDnDDataObject::GetCanonicalFormatEtc(FORMATETC *pFormatEct, FORMATETC *pFormatEtcOut)
411{
412 LogFlowFunc(("\n"));
413
414 /* Set this to NULL in any case. */
415 pFormatEtcOut->ptd = NULL;
416 return E_NOTIMPL;
417}
418
419STDMETHODIMP VBoxDnDDataObject::SetData(FORMATETC *pFormatEtc, STGMEDIUM *pMedium, BOOL fRelease)
420{
421 return E_NOTIMPL;
422}
423
424STDMETHODIMP VBoxDnDDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppEnumFormatEtc)
425{
426 LogFlowFunc(("dwDirection=%RI32, mcFormats=%RI32, mpFormatEtc=%p\n",
427 dwDirection, mcFormats, mpFormatEtc));
428
429 HRESULT hr;
430 if (dwDirection == DATADIR_GET)
431 {
432 hr = VBoxDnDEnumFormatEtc::CreateEnumFormatEtc(mcFormats, mpFormatEtc, ppEnumFormatEtc);
433 }
434 else
435 hr = E_NOTIMPL;
436
437 LogFlowFunc(("hr=%Rhrc\n", hr));
438 return hr;
439}
440
441STDMETHODIMP VBoxDnDDataObject::DAdvise(FORMATETC *pFormatEtc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection)
442{
443 return OLE_E_ADVISENOTSUPPORTED;
444}
445
446STDMETHODIMP VBoxDnDDataObject::DUnadvise(DWORD dwConnection)
447{
448 return OLE_E_ADVISENOTSUPPORTED;
449}
450
451STDMETHODIMP VBoxDnDDataObject::EnumDAdvise(IEnumSTATDATA **ppEnumAdvise)
452{
453 return OLE_E_ADVISENOTSUPPORTED;
454}
455
456/*
457 * Own stuff.
458 */
459
460int VBoxDnDDataObject::Abort(void)
461{
462 LogFlowFunc(("Aborting ...\n"));
463 mStatus = Aborted;
464 return RTSemEventSignal(mSemEvent);
465}
466
467/* static */
468const char* VBoxDnDDataObject::ClipboardFormatToString(CLIPFORMAT fmt)
469{
470 char szFormat[128];
471 if (GetClipboardFormatName(fmt, szFormat, sizeof(szFormat)))
472 LogFlowFunc(("wFormat=%RI16, szName=%s\n", fmt, szFormat));
473
474 switch (fmt)
475 {
476
477 case 1:
478 return "CF_TEXT";
479 case 2:
480 return "CF_BITMAP";
481 case 3:
482 return "CF_METAFILEPICT";
483 case 4:
484 return "CF_SYLK";
485 case 5:
486 return "CF_DIF";
487 case 6:
488 return "CF_TIFF";
489 case 7:
490 return "CF_OEMTEXT";
491 case 8:
492 return "CF_DIB";
493 case 9:
494 return "CF_PALETTE";
495 case 10:
496 return "CF_PENDATA";
497 case 11:
498 return "CF_RIFF";
499 case 12:
500 return "CF_WAVE";
501 case 13:
502 return "CF_UNICODETEXT";
503 case 14:
504 return "CF_ENHMETAFILE";
505 case 15:
506 return "CF_HDROP";
507 case 16:
508 return "CF_LOCALE";
509 case 17:
510 return "CF_DIBV5";
511 case 18:
512 return "CF_MAX";
513 case 49158:
514 return "FileName";
515 case 49159:
516 return "FileNameW";
517 case 49161:
518 return "DATAOBJECT";
519 case 49171:
520 return "Ole Private Data";
521 case 49314:
522 return "Shell Object Offsets";
523 case 49316:
524 return "File Contents";
525 case 49317:
526 return "File Group Descriptor";
527 case 49323:
528 return "Preferred Drop Effect";
529 case 49380:
530 return "Shell Object Offsets";
531 case 49382:
532 return "FileContents";
533 case 49383:
534 return "FileGroupDescriptor";
535 case 49389:
536 return "Preferred DropEffect";
537 case 49268:
538 return "Shell IDList Array";
539 case 49619:
540 return "RenPrivateFileAttachments";
541 default:
542 break;
543 }
544
545 return "unknown";
546}
547
548bool VBoxDnDDataObject::LookupFormatEtc(FORMATETC *pFormatEtc, ULONG *puIndex)
549{
550 AssertReturn(pFormatEtc, false);
551 /* puIndex is optional. */
552
553 for (ULONG i = 0; i < mcFormats; i++)
554 {
555 if( (pFormatEtc->tymed & mpFormatEtc[i].tymed)
556 && pFormatEtc->cfFormat == mpFormatEtc[i].cfFormat
557 && pFormatEtc->dwAspect == mpFormatEtc[i].dwAspect)
558 {
559 LogFlowFunc(("Format found: tyMed=%RI32, cfFormat=%RI16, sFormats=%s, dwAspect=%RI32, ulIndex=%RU32\n",
560 pFormatEtc->tymed, pFormatEtc->cfFormat, VBoxDnDDataObject::ClipboardFormatToString(mpFormatEtc[i].cfFormat),
561 pFormatEtc->dwAspect, i));
562 if (puIndex)
563 *puIndex = i;
564 return true;
565 }
566 }
567
568 LogFlowFunc(("Format NOT found: tyMed=%RI32, cfFormat=%RI16, sFormats=%s, dwAspect=%RI32\n",
569 pFormatEtc->tymed, pFormatEtc->cfFormat, VBoxDnDDataObject::ClipboardFormatToString(pFormatEtc->cfFormat),
570 pFormatEtc->dwAspect));
571 return false;
572
573}
574
575/* static */
576HGLOBAL VBoxDnDDataObject::MemDup(HGLOBAL hMemSource)
577{
578 DWORD dwLen = GlobalSize(hMemSource);
579 AssertReturn(dwLen, NULL);
580 PVOID pvSource = GlobalLock(hMemSource);
581 if (pvSource)
582 {
583 PVOID pvDest = GlobalAlloc(GMEM_FIXED, dwLen);
584 if (pvDest)
585 memcpy(pvDest, pvSource, dwLen);
586
587 GlobalUnlock(hMemSource);
588 return pvDest;
589 }
590
591 return NULL;
592}
593
594void VBoxDnDDataObject::RegisterFormat(FORMATETC *pFormatEtc, CLIPFORMAT clipFormat,
595 TYMED tyMed, LONG lIndex, DWORD dwAspect,
596 DVTARGETDEVICE *pTargetDevice)
597{
598 AssertPtr(pFormatEtc);
599
600 pFormatEtc->cfFormat = clipFormat;
601 pFormatEtc->tymed = tyMed;
602 pFormatEtc->lindex = lIndex;
603 pFormatEtc->dwAspect = dwAspect;
604 pFormatEtc->ptd = pTargetDevice;
605
606 LogFlowFunc(("Registered format=%ld, sFormat=%s\n",
607 pFormatEtc->cfFormat, VBoxDnDDataObject::ClipboardFormatToString(pFormatEtc->cfFormat)));
608}
609
610void VBoxDnDDataObject::SetStatus(Status status)
611{
612 LogFlowFunc(("Setting status to %ld\n", status));
613 mStatus = status;
614}
615
616int VBoxDnDDataObject::Signal(const RTCString &strFormat,
617 const void *pvData, uint32_t cbData)
618{
619 LogFlowFunc(("Signalling ...\n"));
620
621 int rc;
622
623 mStatus = Dropped;
624 mstrFormat = strFormat;
625 if (cbData)
626 {
627 mpvData = RTMemAlloc(cbData);
628 if (mpvData)
629 {
630 memcpy(mpvData, pvData, cbData);
631 mcbData = cbData;
632 rc = VINF_SUCCESS;
633 }
634 else
635 rc = VERR_NO_MEMORY;
636 }
637 else
638 rc = VINF_SUCCESS;
639
640 if (RT_FAILURE(rc))
641 mStatus = Aborted;
642
643 /* Signal in any case. */
644 int rc2 = RTSemEventSignal(mSemEvent);
645 if (RT_SUCCESS(rc))
646 rc = rc2;
647
648 return rc;
649}
650
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette