VirtualBox

source: vbox/trunk/src/VBox/GuestHost/SharedClipboard/clipboard-win.cpp@ 79088

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

Shared Clipboard/URI: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.3 KB
Line 
1/* $Id: clipboard-win.cpp 79088 2019-06-11 12:16:49Z vboxsync $ */
2/** @file
3 * Shared Clipboard: Windows-specific functions for clipboard handling.
4 */
5
6/*
7 * Copyright (C) 2006-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#include <iprt/alloc.h>
19#include <iprt/assert.h>
20#include <iprt/errcore.h>
21#include <iprt/ldr.h>
22#include <iprt/thread.h>
23
24#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
25# include <iprt/win/windows.h>
26# include <iprt/win/shlobj.h> /* For CFSTR_FILEDESCRIPTORXXX + CFSTR_FILECONTENTS. */
27# include <iprt/utf16.h>
28#endif
29
30#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
31#include <VBox/log.h>
32
33#include <VBox/GuestHost/SharedClipboard.h>
34#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
35# include <VBox/GuestHost/SharedClipboard-uri.h>
36#endif
37#include <VBox/GuestHost/SharedClipboard-win.h>
38#include <VBox/GuestHost/clipboard-helper.h>
39
40/**
41 * Opens the clipboard of a specific window.
42 *
43 * @returns VBox status code.
44 * @param hWnd Handle of window to open clipboard for.
45 */
46int VBoxClipboardWinOpen(HWND hWnd)
47{
48 /* "OpenClipboard fails if another window has the clipboard open."
49 * So try a few times and wait up to 1 second.
50 */
51 BOOL fOpened = FALSE;
52
53 LogFlowFunc(("hWnd=%p\n", hWnd));
54
55 int i = 0;
56 for (;;)
57 {
58 if (OpenClipboard(hWnd))
59 {
60 fOpened = TRUE;
61 break;
62 }
63
64 if (i >= 10) /* sleep interval = [1..512] ms */
65 break;
66
67 RTThreadSleep(1 << i);
68 ++i;
69 }
70
71#ifdef LOG_ENABLED
72 if (i > 0)
73 LogFlowFunc(("%d times tried to open clipboard\n", i + 1));
74#endif
75
76 int rc;
77 if (fOpened)
78 rc = VINF_SUCCESS;
79 else
80 {
81 const DWORD dwLastErr = GetLastError();
82 rc = RTErrConvertFromWin32(dwLastErr);
83 LogFunc(("Failed to open clipboard, rc=%Rrc (0x%x)\n", rc, dwLastErr));
84 }
85
86 return rc;
87}
88
89/**
90 * Closes the clipboard for the current thread.
91 *
92 * @returns VBox status code.
93 */
94int VBoxClipboardWinClose(void)
95{
96 int rc;
97
98 LogFlowFuncEnter();
99
100 const BOOL fRc = CloseClipboard();
101 if (RT_UNLIKELY(!fRc))
102 {
103 const DWORD dwLastErr = GetLastError();
104 if (dwLastErr == ERROR_CLIPBOARD_NOT_OPEN)
105 rc = VERR_INVALID_STATE;
106 else
107 rc = RTErrConvertFromWin32(dwLastErr);
108
109 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
110 }
111 else
112 rc = VINF_SUCCESS;
113
114 return rc;
115}
116
117/**
118 * Clears the clipboard for the current thread.
119 *
120 * @returns VBox status code.
121 */
122int VBoxClipboardWinClear(void)
123{
124 int rc;
125
126 LogFlowFuncEnter();
127
128 const BOOL fRc = EmptyClipboard();
129 if (RT_UNLIKELY(!fRc))
130 {
131 const DWORD dwLastErr = GetLastError();
132 if (dwLastErr == ERROR_CLIPBOARD_NOT_OPEN)
133 rc = VERR_INVALID_STATE;
134 else
135 rc = RTErrConvertFromWin32(dwLastErr);
136
137 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
138 }
139 else
140 rc = VINF_SUCCESS;
141
142 return rc;
143}
144
145/**
146 * Checks and initializes function pointer which are required for using
147 * the new clipboard API.
148 *
149 * @returns VBox status code.
150 * @param pAPI Where to store the retrieved function pointers.
151 * Will be set to NULL if the new API is not available.
152 */
153int VBoxClipboardWinCheckAndInitNewAPI(PVBOXCLIPBOARDWINAPINEW pAPI)
154{
155 RTLDRMOD hUser32 = NIL_RTLDRMOD;
156 int rc = RTLdrLoadSystem("User32.dll", /* fNoUnload = */ true, &hUser32);
157 if (RT_SUCCESS(rc))
158 {
159 rc = RTLdrGetSymbol(hUser32, "AddClipboardFormatListener", (void **)&pAPI->pfnAddClipboardFormatListener);
160 if (RT_SUCCESS(rc))
161 {
162 rc = RTLdrGetSymbol(hUser32, "RemoveClipboardFormatListener", (void **)&pAPI->pfnRemoveClipboardFormatListener);
163 }
164
165 RTLdrClose(hUser32);
166 }
167
168 if (RT_SUCCESS(rc))
169 {
170 LogFunc(("New Clipboard API enabled\n"));
171 }
172 else
173 {
174 RT_BZERO(pAPI, sizeof(VBOXCLIPBOARDWINAPINEW));
175 LogFunc(("New Clipboard API not available; rc=%Rrc\n", rc));
176 }
177
178 return rc;
179}
180
181/**
182 * Returns if the new clipboard API is available or not.
183 *
184 * @returns @c true if the new API is available, or @c false if not.
185 * @param pAPI Structure used for checking if the new clipboard API is available or not.
186 */
187bool VBoxClipboardWinIsNewAPI(PVBOXCLIPBOARDWINAPINEW pAPI)
188{
189 if (!pAPI)
190 return false;
191 return pAPI->pfnAddClipboardFormatListener != NULL;
192}
193
194/**
195 * Adds ourselves into the chain of cliboard listeners.
196 *
197 * @returns VBox status code.
198 * @param pCtx Windows clipboard context to use to add ourselves.
199 */
200int VBoxClipboardWinAddToCBChain(PVBOXCLIPBOARDWINCTX pCtx)
201{
202 const PVBOXCLIPBOARDWINAPINEW pAPI = &pCtx->newAPI;
203
204 BOOL fRc;
205 if (VBoxClipboardWinIsNewAPI(pAPI))
206 {
207 fRc = pAPI->pfnAddClipboardFormatListener(pCtx->hWnd);
208 }
209 else
210 {
211 pCtx->hWndNextInChain = SetClipboardViewer(pCtx->hWnd);
212 fRc = pCtx->hWndNextInChain != NULL;
213 }
214
215 int rc = VINF_SUCCESS;
216
217 if (!fRc)
218 {
219 const DWORD dwLastErr = GetLastError();
220 rc = RTErrConvertFromWin32(dwLastErr);
221 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
222 }
223
224 return rc;
225}
226
227/**
228 * Remove ourselves from the chain of cliboard listeners
229 *
230 * @returns VBox status code.
231 * @param pCtx Windows clipboard context to use to remove ourselves.
232 */
233int VBoxClipboardWinRemoveFromCBChain(PVBOXCLIPBOARDWINCTX pCtx)
234{
235 if (!pCtx->hWnd)
236 return VINF_SUCCESS;
237
238 const PVBOXCLIPBOARDWINAPINEW pAPI = &pCtx->newAPI;
239
240 BOOL fRc;
241 if (VBoxClipboardWinIsNewAPI(pAPI))
242 {
243 fRc = pAPI->pfnRemoveClipboardFormatListener(pCtx->hWnd);
244 }
245 else
246 {
247 fRc = ChangeClipboardChain(pCtx->hWnd, pCtx->hWndNextInChain);
248 if (fRc)
249 pCtx->hWndNextInChain = NULL;
250 }
251
252 int rc = VINF_SUCCESS;
253
254 if (!fRc)
255 {
256 const DWORD dwLastErr = GetLastError();
257 rc = RTErrConvertFromWin32(dwLastErr);
258 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
259 }
260
261 return rc;
262}
263
264/**
265 * Callback which is invoked when we have successfully pinged ourselves down the
266 * clipboard chain. We simply unset a boolean flag to say that we are responding.
267 * There is a race if a ping returns after the next one is initiated, but nothing
268 * very bad is likely to happen.
269 *
270 * @param hWnd Window handle to use for this callback. Not used currently.
271 * @param uMsg Message to handle. Not used currently.
272 * @param dwData Pointer to user-provided data. Contains our Windows clipboard context.
273 * @param lResult Additional data to pass. Not used currently.
274 */
275VOID CALLBACK VBoxClipboardWinChainPingProc(HWND hWnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult)
276{
277 RT_NOREF(hWnd);
278 RT_NOREF(uMsg);
279 RT_NOREF(lResult);
280
281 /** @todo r=andy Why not using SetWindowLongPtr for keeping the context? */
282 PVBOXCLIPBOARDWINCTX pCtx = (PVBOXCLIPBOARDWINCTX)dwData;
283 AssertPtrReturnVoid(pCtx);
284
285 pCtx->oldAPI.fCBChainPingInProcess = FALSE;
286}
287
288/**
289 * Converts a (registered or standard) Windows clipboard format to a VBox clipboard format.
290 *
291 * @returns Converted VBox clipboard format, or VBOX_SHARED_CLIPBOARD_FMT_NONE if not found.
292 * @param uFormat Windows clipboard format to convert.
293 */
294VBOXCLIPBOARDFORMAT VBoxClipboardWinClipboardFormatToVBox(UINT uFormat)
295{
296 /* Insert the requested clipboard format data into the clipboard. */
297 VBOXCLIPBOARDFORMAT vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_NONE;
298
299 switch (uFormat)
300 {
301 case CF_UNICODETEXT:
302 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
303 break;
304
305 case CF_DIB:
306 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
307 break;
308
309#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
310 /* CF_HDROP handles file system entries which are locally present
311 * on source for transferring to the target.
312 *
313 * This does *not* invoke any IDataObject / IStream implementations! */
314 case CF_HDROP:
315 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_URI_LIST;
316 break;
317#endif
318
319 default:
320 if (uFormat >= 0xC000) /** Formats registered with RegisterClipboardFormat() start at this index. */
321 {
322 TCHAR szFormatName[256]; /** @todo r=andy Do we need Unicode support here as well? */
323 int cActual = GetClipboardFormatName(uFormat, szFormatName, sizeof(szFormatName) / sizeof(TCHAR));
324 if (cActual)
325 {
326 LogFlowFunc(("uFormat=%u -> szFormatName=%s\n", uFormat, szFormatName));
327
328 if (RTStrCmp(szFormatName, VBOX_CLIPBOARD_WIN_REGFMT_HTML) == 0)
329 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_HTML;
330#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
331 /* These types invoke our IDataObject / IStream implementations. */
332 else if ( (RTStrCmp(szFormatName, CFSTR_FILEDESCRIPTORA) == 0)
333 || (RTStrCmp(szFormatName, CFSTR_FILECONTENTS) == 0))
334 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_URI_LIST;
335 /** @todo Do we need to handle CFSTR_FILEDESCRIPTORW here as well? */
336#endif
337 }
338 }
339 break;
340 }
341
342 LogFlowFunc(("uFormat=%u -> vboxFormat=0x%x\n", uFormat, vboxFormat));
343 return vboxFormat;
344}
345
346/**
347 * Retrieves all supported clipboard formats of a specific clipboard.
348 *
349 * @returns VBox status code.
350 * @param pCtx Windows clipboard context to retrieve formats for.
351 * @param pfFormats Where to store the retrieved formats of type VBOX_SHARED_CLIPBOARD_FMT_ (bitmask).
352 */
353int VBoxClipboardWinGetFormats(PVBOXCLIPBOARDWINCTX pCtx, PVBOXCLIPBOARDFORMATS pfFormats)
354{
355 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
356 AssertPtrReturn(pfFormats, VERR_INVALID_POINTER);
357
358 VBOXCLIPBOARDFORMATS fFormats = VBOX_SHARED_CLIPBOARD_FMT_NONE;
359
360 /* Query list of available formats and report to host. */
361 int rc = VBoxClipboardWinOpen(pCtx->hWnd);
362 if (RT_SUCCESS(rc))
363 {
364 UINT uCurFormat = 0; /* Must be set to zero for EnumClipboardFormats(). */
365 while ((uCurFormat = EnumClipboardFormats(uCurFormat)) != 0)
366 fFormats |= VBoxClipboardWinClipboardFormatToVBox(uCurFormat);
367
368 int rc2 = VBoxClipboardWinClose();
369 AssertRC(rc2);
370 }
371
372 if (RT_FAILURE(rc))
373 {
374 LogFunc(("Failed with rc=%Rrc\n", rc));
375 }
376 else
377 {
378 LogFlowFunc(("pfFormats=0x%08X\n", pfFormats));
379 *pfFormats = fFormats;
380 }
381
382 return rc;
383}
384
385/**
386 * Extracts a field value from CF_HTML data.
387 *
388 * @returns VBox status code.
389 * @param pszSrc source in CF_HTML format.
390 * @param pszOption Name of CF_HTML field.
391 * @param puValue Where to return extracted value of CF_HTML field.
392 */
393int VBoxClipboardWinGetCFHTMLHeaderValue(const char *pszSrc, const char *pszOption, uint32_t *puValue)
394{
395 AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
396 AssertPtrReturn(pszOption, VERR_INVALID_POINTER);
397
398 int rc = VERR_INVALID_PARAMETER;
399
400 const char *pszOptionValue = RTStrStr(pszSrc, pszOption);
401 if (pszOptionValue)
402 {
403 size_t cchOption = strlen(pszOption);
404 Assert(cchOption);
405
406 rc = RTStrToUInt32Ex(pszOptionValue + cchOption, NULL, 10, puValue);
407 }
408 return rc;
409}
410
411/**
412 * Check that the source string contains CF_HTML struct.
413 *
414 * @returns @c true if the @a pszSource string is in CF_HTML format.
415 * @param pszSource Source string to check.
416 */
417bool VBoxClipboardWinIsCFHTML(const char *pszSource)
418{
419 return RTStrStr(pszSource, "Version:") != NULL
420 && RTStrStr(pszSource, "StartHTML:") != NULL;
421}
422
423/**
424 * Converts clipboard data from CF_HTML format to MIME clipboard format.
425 *
426 * Returns allocated buffer that contains html converted to text/html mime type
427 *
428 * @returns VBox status code.
429 * @param pszSource The input.
430 * @param cch The length of the input.
431 * @param ppszOutput Where to return the result. Free using RTMemFree.
432 * @param pcbOutput Where to the return length of the result (bytes/chars).
433 */
434int VBoxClipboardWinConvertCFHTMLToMIME(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pcbOutput)
435{
436 Assert(pszSource);
437 Assert(cch);
438 Assert(ppszOutput);
439 Assert(pcbOutput);
440
441 uint32_t offStart;
442 int rc = VBoxClipboardWinGetCFHTMLHeaderValue(pszSource, "StartFragment:", &offStart);
443 if (RT_SUCCESS(rc))
444 {
445 uint32_t offEnd;
446 rc = VBoxClipboardWinGetCFHTMLHeaderValue(pszSource, "EndFragment:", &offEnd);
447 if (RT_SUCCESS(rc))
448 {
449 if ( offStart > 0
450 && offEnd > 0
451 && offEnd > offStart
452 && offEnd <= cch)
453 {
454 uint32_t cchSubStr = offEnd - offStart;
455 char *pszResult = (char *)RTMemAlloc(cchSubStr + 1);
456 if (pszResult)
457 {
458 rc = RTStrCopyEx(pszResult, cchSubStr + 1, pszSource + offStart, cchSubStr);
459 if (RT_SUCCESS(rc))
460 {
461 *ppszOutput = pszResult;
462 *pcbOutput = (uint32_t)(cchSubStr + 1);
463 rc = VINF_SUCCESS;
464 }
465 else
466 {
467 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
468 RTMemFree(pszResult);
469 }
470 }
471 else
472 {
473 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment\n"));
474 rc = VERR_NO_MEMORY;
475 }
476 }
477 else
478 {
479 LogRelFlowFunc(("Error: CF_HTML out of bounds - offStart=%#x offEnd=%#x cch=%#x\n", offStart, offEnd, cch));
480 rc = VERR_INVALID_PARAMETER;
481 }
482 }
483 else
484 {
485 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
486 rc = VERR_INVALID_PARAMETER;
487 }
488 }
489 else
490 {
491 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected StartFragment. rc = %Rrc\n", rc));
492 rc = VERR_INVALID_PARAMETER;
493 }
494
495 return rc;
496}
497
498/**
499 * Converts source UTF-8 MIME HTML clipboard data to UTF-8 CF_HTML format.
500 *
501 * This is just encapsulation work, slapping a header on the data.
502 *
503 * It allocates [..]
504 *
505 * Calculations:
506 * Header length = format Length + (2*(10 - 5('%010d'))('digits')) - 2('%s') = format length + 8
507 * EndHtml = Header length + fragment length
508 * StartHtml = 105(constant)
509 * StartFragment = 141(constant) may vary if the header html content will be extended
510 * EndFragment = Header length + fragment length - 38(ending length)
511 *
512 * @param pszSource Source buffer that contains utf-16 string in mime html format
513 * @param cb Size of source buffer in bytes
514 * @param ppszOutput Where to return the allocated output buffer to put converted UTF-8
515 * CF_HTML clipboard data. This function allocates memory for this.
516 * @param pcbOutput Where to return the size of allocated result buffer in bytes/chars, including zero terminator
517 *
518 * @note output buffer should be free using RTMemFree()
519 * @note Everything inside of fragment can be UTF8. Windows allows it. Everything in header should be Latin1.
520 */
521int VBoxClipboardWinConvertMIMEToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput)
522{
523 Assert(ppszOutput);
524 Assert(pcbOutput);
525 Assert(pszSource);
526 Assert(cb);
527
528 /* construct CF_HTML formatted string */
529 char *pszResult = NULL;
530 size_t cchFragment;
531 int rc = RTStrNLenEx(pszSource, cb, &cchFragment);
532 if (!RT_SUCCESS(rc))
533 {
534 LogRelFlowFunc(("Error: invalid source fragment. rc = %Rrc\n"));
535 return VERR_INVALID_PARAMETER;
536 }
537
538 /*
539 @StartHtml - pos before <html>
540 @EndHtml - whole size of text excluding ending zero char
541 @StartFragment - pos after <!--StartFragment-->
542 @EndFragment - pos before <!--EndFragment-->
543 @note: all values includes CR\LF inserted into text
544 Calculations:
545 Header length = format Length + (3*6('digits')) - 2('%s') = format length + 16 (control value - 183)
546 EndHtml = Header length + fragment length
547 StartHtml = 105(constant)
548 StartFragment = 143(constant)
549 EndFragment = Header length + fragment length - 40(ending length)
550 */
551 static const char s_szFormatSample[] =
552 /* 0: */ "Version:1.0\r\n"
553 /* 13: */ "StartHTML:000000101\r\n"
554 /* 34: */ "EndHTML:%0000009u\r\n" // END HTML = Header length + fragment length
555 /* 53: */ "StartFragment:000000137\r\n"
556 /* 78: */ "EndFragment:%0000009u\r\n"
557 /* 101: */ "<html>\r\n"
558 /* 109: */ "<body>\r\n"
559 /* 117: */ "<!--StartFragment-->"
560 /* 137: */ "%s"
561 /* 137+2: */ "<!--EndFragment-->\r\n"
562 /* 157+2: */ "</body>\r\n"
563 /* 166+2: */ "</html>\r\n";
564 /* 175+2: */
565 AssertCompile(sizeof(s_szFormatSample) == 175 + 2 + 1);
566
567 /* calculate parameters of CF_HTML header */
568 size_t cchHeader = sizeof(s_szFormatSample) - 1;
569 size_t offEndHtml = cchHeader + cchFragment;
570 size_t offEndFragment = cchHeader + cchFragment - 38; /* 175-137 = 38 */
571 pszResult = (char *)RTMemAlloc(offEndHtml + 1);
572 if (pszResult == NULL)
573 {
574 LogRelFlowFunc(("Error: Cannot allocate memory for result buffer. rc = %Rrc\n"));
575 return VERR_NO_MEMORY;
576 }
577
578 /* format result CF_HTML string */
579 size_t cchFormatted = RTStrPrintf(pszResult, offEndHtml + 1,
580 s_szFormatSample, offEndHtml, offEndFragment, pszSource);
581 Assert(offEndHtml == cchFormatted); NOREF(cchFormatted);
582
583#ifdef VBOX_STRICT
584 /* Control calculations. check consistency.*/
585 static const char s_szStartFragment[] = "<!--StartFragment-->";
586 static const char s_szEndFragment[] = "<!--EndFragment-->";
587
588 /* check 'StartFragment:' value */
589 const char *pszRealStartFragment = RTStrStr(pszResult, s_szStartFragment);
590 Assert(&pszRealStartFragment[sizeof(s_szStartFragment) - 1] - pszResult == 137);
591
592 /* check 'EndFragment:' value */
593 const char *pszRealEndFragment = RTStrStr(pszResult, s_szEndFragment);
594 Assert((size_t)(pszRealEndFragment - pszResult) == offEndFragment);
595#endif
596
597 *ppszOutput = pszResult;
598 *pcbOutput = (uint32_t)cchFormatted + 1;
599 Assert(*pcbOutput == cchFormatted + 1);
600
601 return VINF_SUCCESS;
602}
603
604int VBoxClipboardWinHandleWMSetFormats(const PVBOXCLIPBOARDWINCTX pCtx, VBOXCLIPBOARDFORMATS fFormats)
605{
606 RT_NOREF(pCtx);
607
608 LogFunc(("fFormats=0x%x\n", fFormats));
609
610 HANDLE hClip = NULL;
611 UINT cfFormat = 0;
612
613 int rc = VINF_SUCCESS;
614
615 /** @todo r=andy Only one clipboard format can be set at once, at least on Windows. */
616 /** @todo Implement more flexible clipboard precedence for supported formats. */
617
618 if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
619 {
620 LogFunc(("CF_UNICODETEXT\n"));
621 hClip = SetClipboardData(CF_UNICODETEXT, NULL);
622 }
623 else if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
624 {
625 LogFunc(("CF_DIB\n"));
626 hClip = SetClipboardData(CF_DIB, NULL);
627 }
628 else if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
629 {
630 LogFunc(("VBOX_CLIPBOARD_WIN_REGFMT_HTML\n"));
631 cfFormat = RegisterClipboardFormat(VBOX_CLIPBOARD_WIN_REGFMT_HTML);
632 if (cfFormat != 0)
633 hClip = SetClipboardData(cfFormat, NULL);
634 }
635 else
636 {
637 rc = VERR_NOT_SUPPORTED;
638 }
639
640 LogFlowFuncLeaveRC(rc);
641 return rc;
642}
643
644#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
645int VBoxClipboardWinURIHandleWMSetFormats(const PVBOXCLIPBOARDWINCTX pWinCtx, PSHAREDCLIPBOARDURICTX pURICtx,
646 PSHAREDCLIPBOARDPROVIDERCREATIONCTX pProviderCtx,
647 VBOXCLIPBOARDFORMATS fFormats)
648{
649 AssertPtrReturn(pWinCtx, VERR_INVALID_POINTER);
650 AssertPtrReturn(pURICtx, VERR_INVALID_POINTER);
651 AssertPtrReturn(pProviderCtx, VERR_INVALID_POINTER);
652
653 RT_NOREF(pWinCtx);
654
655 if (!(fFormats & VBOX_SHARED_CLIPBOARD_FMT_URI_LIST))
656 return VERR_NOT_SUPPORTED;
657
658 int rc;
659
660 const uint32_t cTransfers = SharedClipboardURICtxGetActiveTransfers(pURICtx);
661
662 LogFunc(("cTransfers=%RU32\n", cTransfers));
663
664 if (cTransfers == 0) /* Only allow one transfer at a time for now. */
665 {
666 PSHAREDCLIPBOARDURITRANSFER pTransfer;
667 rc = SharedClipboardURITransferCreate(SHAREDCLIPBOARDURITRANSFERDIR_WRITE, pProviderCtx, &pTransfer);
668 if (RT_SUCCESS(rc))
669 rc = SharedClipboardURICtxTransferAdd(pURICtx, pTransfer);
670
671 SharedClipboardWinURITransferCtx *pWinURITransferCtx = new SharedClipboardWinURITransferCtx();
672
673 pTransfer->pvUser = pWinURITransferCtx;
674 pTransfer->cbUser = sizeof(SharedClipboardWinURITransferCtx);
675
676 pWinURITransferCtx->pDataObj = new VBoxClipboardWinDataObject(pTransfer);
677 if (pWinURITransferCtx->pDataObj)
678 {
679 rc = pWinURITransferCtx->pDataObj->Init();
680 if (RT_SUCCESS(rc))
681 {
682 VBoxClipboardWinClose();
683 /* Note: Clipboard must be closed first before calling OleSetClipboard(). */
684
685 /** @todo There is a potential race between VBoxClipboardWinClose() and OleSetClipboard(),
686 * where another application could own the clipboard (open), and thus the call to
687 * OleSetClipboard() will fail. Needs (better) fixing. */
688 for (unsigned uTries = 0; uTries < 3; uTries++)
689 {
690 HRESULT hr = OleSetClipboard(pWinURITransferCtx->pDataObj);
691 if (SUCCEEDED(hr))
692 {
693 pURICtx->cTransfers++;
694 break;
695 }
696 else
697 {
698 rc = VERR_ACCESS_DENIED; /** @todo Fudge; fix this. */
699 LogRel(("Clipboard: Failed with %Rhrc when setting data object to clipboard\n", hr));
700 RTThreadSleep(100); /* Wait a bit. */
701 }
702 }
703 }
704 }
705 else
706 rc = VERR_NO_MEMORY;
707 }
708 else
709 {
710 rc = VERR_ALREADY_EXISTS; /** @todo Fudge; fix this. */
711 LogRel(("Clipboard: Only one transfer at a time supported (current %RU32 transfer(s) active), skipping\n",
712 pURICtx->cTransfers));
713 }
714
715 LogFlowFuncLeaveRC(rc);
716 return rc;
717}
718
719/**
720 * Converts a DROPFILES (HDROP) structure to a string list, separated by \r\n.
721 * Does not do any locking on the input data.
722 *
723 * @returns VBox status code.
724 * @param pDropFiles Pointer to DROPFILES structure to convert.
725 * @param ppszData Where to return the converted (allocated) data on success.
726 * Must be free'd by the caller with RTMemFree().
727 * @param pcbData Size (in bytes) of the allocated data returned.
728 */
729int VBoxClipboardWinDropFilesToStringList(DROPFILES *pDropFiles, char **ppszbData, size_t *pcbData)
730{
731 AssertPtrReturn(pDropFiles, VERR_INVALID_POINTER);
732 AssertPtrReturn(ppszbData, VERR_INVALID_POINTER);
733 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
734
735 /* Do we need to do Unicode stuff? */
736 const bool fUnicode = RT_BOOL(pDropFiles->fWide);
737
738 /* Get the offset of the file list. */
739 Assert(pDropFiles->pFiles >= sizeof(DROPFILES));
740
741 /* Note: This is *not* pDropFiles->pFiles! DragQueryFile only
742 * will work with the plain storage medium pointer! */
743 HDROP hDrop = (HDROP)(pDropFiles);
744
745 int rc = VINF_SUCCESS;
746
747 /* First, get the file count. */
748 /** @todo Does this work on Windows 2000 / NT4? */
749 char *pszFiles = NULL;
750 uint32_t cchFiles = 0;
751 UINT cFiles = DragQueryFile(hDrop, UINT32_MAX /* iFile */, NULL /* lpszFile */, 0 /* cchFile */);
752
753 LogFlowFunc(("Got %RU16 file(s), fUnicode=%RTbool\n", cFiles, fUnicode));
754
755 for (UINT i = 0; i < cFiles; i++)
756 {
757 UINT cchFile = DragQueryFile(hDrop, i /* File index */, NULL /* Query size first */, 0 /* cchFile */);
758 Assert(cchFile);
759
760 if (RT_FAILURE(rc))
761 break;
762
763 char *pszFileUtf8 = NULL; /* UTF-8 version. */
764 UINT cchFileUtf8 = 0;
765 if (fUnicode)
766 {
767 /* Allocate enough space (including terminator). */
768 WCHAR *pwszFile = (WCHAR *)RTMemAlloc((cchFile + 1) * sizeof(WCHAR));
769 if (pwszFile)
770 {
771 const UINT cwcFileUtf16 = DragQueryFileW(hDrop, i /* File index */,
772 pwszFile, cchFile + 1 /* Include terminator */);
773
774 AssertMsg(cwcFileUtf16 == cchFile, ("cchFileUtf16 (%RU16) does not match cchFile (%RU16)\n",
775 cwcFileUtf16, cchFile));
776 RT_NOREF(cwcFileUtf16);
777
778 rc = RTUtf16ToUtf8(pwszFile, &pszFileUtf8);
779 if (RT_SUCCESS(rc))
780 {
781 cchFileUtf8 = (UINT)strlen(pszFileUtf8);
782 Assert(cchFileUtf8);
783 }
784
785 RTMemFree(pwszFile);
786 }
787 else
788 rc = VERR_NO_MEMORY;
789 }
790 else /* ANSI */
791 {
792 /* Allocate enough space (including terminator). */
793 pszFileUtf8 = (char *)RTMemAlloc((cchFile + 1) * sizeof(char));
794 if (pszFileUtf8)
795 {
796 cchFileUtf8 = DragQueryFileA(hDrop, i /* File index */,
797 pszFileUtf8, cchFile + 1 /* Include terminator */);
798
799 AssertMsg(cchFileUtf8 == cchFile, ("cchFileUtf8 (%RU16) does not match cchFile (%RU16)\n",
800 cchFileUtf8, cchFile));
801 }
802 else
803 rc = VERR_NO_MEMORY;
804 }
805
806 if (RT_SUCCESS(rc))
807 {
808 LogFlowFunc(("\tFile: %s (cchFile=%RU16)\n", pszFileUtf8, cchFileUtf8));
809
810 LogRel(("Shared Clipboard: Adding guest file '%s'\n", pszFileUtf8));
811
812 rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, pszFileUtf8, cchFileUtf8);
813 if (RT_SUCCESS(rc))
814 cchFiles += cchFileUtf8;
815 }
816 else
817 LogFunc(("Error handling file entry #%u, rc=%Rrc\n", i, rc));
818
819 if (pszFileUtf8)
820 RTStrFree(pszFileUtf8);
821
822 if (RT_FAILURE(rc))
823 break;
824
825 /* Add separation between filenames.
826 * Note: Also do this for the last element of the list. */
827 rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, "\r\n", 2 /* Bytes */);
828 if (RT_SUCCESS(rc))
829 cchFiles += 2; /* Include \r\n */
830 }
831
832 if (RT_SUCCESS(rc))
833 {
834 cchFiles += 1; /* Add string termination. */
835 uint32_t cbFiles = cchFiles * sizeof(char);
836
837 LogFlowFunc(("cFiles=%u, cchFiles=%RU32, cbFiles=%RU32, pszFiles=0x%p\n",
838 cFiles, cchFiles, cbFiles, pszFiles));
839
840 /* Translate the list into URI elements. */
841 SharedClipboardURIList lstURI;
842 rc = lstURI.AppendNativePathsFromList(pszFiles, cbFiles,
843 SHAREDCLIPBOARDURILIST_FLAGS_ABSOLUTE_PATHS);
844 if (RT_SUCCESS(rc))
845 {
846 RTCString strRoot = lstURI.GetRootEntries();
847 size_t cbRoot = strRoot.length() + 1; /* Include termination */
848
849 void *pvData = RTMemAlloc(cbRoot);
850 if (pvData)
851 {
852 memcpy(pvData, strRoot.c_str(), cbRoot);
853
854 *ppszbData = (char *)pvData;
855 *pcbData = cbRoot;
856 }
857 else
858 rc = VERR_NO_MEMORY;
859 }
860 }
861
862 LogFlowFunc(("Building CF_HDROP list rc=%Rrc, pszFiles=0x%p, cFiles=%RU16, cchFiles=%RU32\n",
863 rc, pszFiles, cFiles, cchFiles));
864
865 if (pszFiles)
866 RTStrFree(pszFiles);
867
868 return rc;
869}
870#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
871
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