VirtualBox

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

Last change on this file since 79860 was 79672, checked in by vboxsync, 6 years ago

Shared Clipboard/URI: More code for root entries handling.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.1 KB
Line 
1/* $Id: clipboard-win.cpp 79672 2019-07-10 13:02:50Z 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 <iprt/errcore.h>
34
35#include <VBox/GuestHost/SharedClipboard.h>
36#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
37# include <VBox/GuestHost/SharedClipboard-uri.h>
38#endif
39#include <VBox/GuestHost/SharedClipboard-win.h>
40#include <VBox/GuestHost/clipboard-helper.h>
41
42
43/**
44 * Opens the clipboard of a specific window.
45 *
46 * @returns VBox status code.
47 * @param hWnd Handle of window to open clipboard for.
48 */
49int VBoxClipboardWinOpen(HWND hWnd)
50{
51 /* "OpenClipboard fails if another window has the clipboard open."
52 * So try a few times and wait up to 1 second.
53 */
54 BOOL fOpened = FALSE;
55
56 LogFlowFunc(("hWnd=%p\n", hWnd));
57
58 int i = 0;
59 for (;;)
60 {
61 if (OpenClipboard(hWnd))
62 {
63 fOpened = TRUE;
64 break;
65 }
66
67 if (i >= 10) /* sleep interval = [1..512] ms */
68 break;
69
70 RTThreadSleep(1 << i);
71 ++i;
72 }
73
74#ifdef LOG_ENABLED
75 if (i > 0)
76 LogFlowFunc(("%d times tried to open clipboard\n", i + 1));
77#endif
78
79 int rc;
80 if (fOpened)
81 rc = VINF_SUCCESS;
82 else
83 {
84 const DWORD dwLastErr = GetLastError();
85 rc = RTErrConvertFromWin32(dwLastErr);
86 LogFunc(("Failed to open clipboard, rc=%Rrc (0x%x)\n", rc, dwLastErr));
87 }
88
89 return rc;
90}
91
92/**
93 * Closes the clipboard for the current thread.
94 *
95 * @returns VBox status code.
96 */
97int VBoxClipboardWinClose(void)
98{
99 int rc;
100
101 LogFlowFuncEnter();
102
103 const BOOL fRc = CloseClipboard();
104 if (RT_UNLIKELY(!fRc))
105 {
106 const DWORD dwLastErr = GetLastError();
107 if (dwLastErr == ERROR_CLIPBOARD_NOT_OPEN)
108 rc = VERR_INVALID_STATE;
109 else
110 rc = RTErrConvertFromWin32(dwLastErr);
111
112 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
113 }
114 else
115 rc = VINF_SUCCESS;
116
117 return rc;
118}
119
120/**
121 * Clears the clipboard for the current thread.
122 *
123 * @returns VBox status code.
124 */
125int VBoxClipboardWinClear(void)
126{
127 int rc;
128
129 LogFlowFuncEnter();
130
131 const BOOL fRc = EmptyClipboard();
132 if (RT_UNLIKELY(!fRc))
133 {
134 const DWORD dwLastErr = GetLastError();
135 if (dwLastErr == ERROR_CLIPBOARD_NOT_OPEN)
136 rc = VERR_INVALID_STATE;
137 else
138 rc = RTErrConvertFromWin32(dwLastErr);
139
140 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
141 }
142 else
143 rc = VINF_SUCCESS;
144
145 return rc;
146}
147
148/**
149 * Checks and initializes function pointer which are required for using
150 * the new clipboard API.
151 *
152 * @returns VBox status code.
153 * @param pAPI Where to store the retrieved function pointers.
154 * Will be set to NULL if the new API is not available.
155 */
156int VBoxClipboardWinCheckAndInitNewAPI(PVBOXCLIPBOARDWINAPINEW pAPI)
157{
158 RTLDRMOD hUser32 = NIL_RTLDRMOD;
159 int rc = RTLdrLoadSystem("User32.dll", /* fNoUnload = */ true, &hUser32);
160 if (RT_SUCCESS(rc))
161 {
162 rc = RTLdrGetSymbol(hUser32, "AddClipboardFormatListener", (void **)&pAPI->pfnAddClipboardFormatListener);
163 if (RT_SUCCESS(rc))
164 {
165 rc = RTLdrGetSymbol(hUser32, "RemoveClipboardFormatListener", (void **)&pAPI->pfnRemoveClipboardFormatListener);
166 }
167
168 RTLdrClose(hUser32);
169 }
170
171 if (RT_SUCCESS(rc))
172 {
173 LogFunc(("New Clipboard API enabled\n"));
174 }
175 else
176 {
177 RT_BZERO(pAPI, sizeof(VBOXCLIPBOARDWINAPINEW));
178 LogFunc(("New Clipboard API not available; rc=%Rrc\n", rc));
179 }
180
181 return rc;
182}
183
184/**
185 * Returns if the new clipboard API is available or not.
186 *
187 * @returns @c true if the new API is available, or @c false if not.
188 * @param pAPI Structure used for checking if the new clipboard API is available or not.
189 */
190bool VBoxClipboardWinIsNewAPI(PVBOXCLIPBOARDWINAPINEW pAPI)
191{
192 if (!pAPI)
193 return false;
194 return pAPI->pfnAddClipboardFormatListener != NULL;
195}
196
197/**
198 * Adds ourselves into the chain of cliboard listeners.
199 *
200 * @returns VBox status code.
201 * @param pCtx Windows clipboard context to use to add ourselves.
202 */
203int VBoxClipboardWinChainAdd(PVBOXCLIPBOARDWINCTX pCtx)
204{
205 const PVBOXCLIPBOARDWINAPINEW pAPI = &pCtx->newAPI;
206
207 BOOL fRc;
208 if (VBoxClipboardWinIsNewAPI(pAPI))
209 {
210 fRc = pAPI->pfnAddClipboardFormatListener(pCtx->hWnd);
211 }
212 else
213 {
214 pCtx->hWndNextInChain = SetClipboardViewer(pCtx->hWnd);
215 fRc = pCtx->hWndNextInChain != NULL;
216 }
217
218 int rc = VINF_SUCCESS;
219
220 if (!fRc)
221 {
222 const DWORD dwLastErr = GetLastError();
223 rc = RTErrConvertFromWin32(dwLastErr);
224 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
225 }
226
227 return rc;
228}
229
230/**
231 * Remove ourselves from the chain of cliboard listeners
232 *
233 * @returns VBox status code.
234 * @param pCtx Windows clipboard context to use to remove ourselves.
235 */
236int VBoxClipboardWinChainRemove(PVBOXCLIPBOARDWINCTX pCtx)
237{
238 if (!pCtx->hWnd)
239 return VINF_SUCCESS;
240
241 const PVBOXCLIPBOARDWINAPINEW pAPI = &pCtx->newAPI;
242
243 BOOL fRc;
244 if (VBoxClipboardWinIsNewAPI(pAPI))
245 {
246 fRc = pAPI->pfnRemoveClipboardFormatListener(pCtx->hWnd);
247 }
248 else
249 {
250 fRc = ChangeClipboardChain(pCtx->hWnd, pCtx->hWndNextInChain);
251 if (fRc)
252 pCtx->hWndNextInChain = NULL;
253 }
254
255 int rc = VINF_SUCCESS;
256
257 if (!fRc)
258 {
259 const DWORD dwLastErr = GetLastError();
260 rc = RTErrConvertFromWin32(dwLastErr);
261 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
262 }
263
264 return rc;
265}
266
267/**
268 * Callback which is invoked when we have successfully pinged ourselves down the
269 * clipboard chain. We simply unset a boolean flag to say that we are responding.
270 * There is a race if a ping returns after the next one is initiated, but nothing
271 * very bad is likely to happen.
272 *
273 * @param hWnd Window handle to use for this callback. Not used currently.
274 * @param uMsg Message to handle. Not used currently.
275 * @param dwData Pointer to user-provided data. Contains our Windows clipboard context.
276 * @param lResult Additional data to pass. Not used currently.
277 */
278VOID CALLBACK VBoxClipboardWinChainPingProc(HWND hWnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult)
279{
280 RT_NOREF(hWnd);
281 RT_NOREF(uMsg);
282 RT_NOREF(lResult);
283
284 /** @todo r=andy Why not using SetWindowLongPtr for keeping the context? */
285 PVBOXCLIPBOARDWINCTX pCtx = (PVBOXCLIPBOARDWINCTX)dwData;
286 AssertPtrReturnVoid(pCtx);
287
288 pCtx->oldAPI.fCBChainPingInProcess = FALSE;
289}
290
291/**
292 * Passes a window message to the next window in the clipboard chain.
293 *
294 * @returns LRESULT
295 * @param pWinCtx Window context to use.
296 * @param msg Window message to pass.
297 * @param wParam WPARAM to pass.
298 * @param lParam LPARAM to pass.
299 */
300LRESULT VBoxClipboardWinChainPassToNext(PVBOXCLIPBOARDWINCTX pWinCtx,
301 UINT msg, WPARAM wParam, LPARAM lParam)
302{
303 LogFlowFuncEnter();
304
305 LRESULT lresultRc = 0;
306
307 if (pWinCtx->hWndNextInChain)
308 {
309 LogFunc(("hWndNextInChain=%p\n", pWinCtx->hWndNextInChain));
310
311 /* Pass the message to next window in the clipboard chain. */
312 DWORD_PTR dwResult;
313 lresultRc = SendMessageTimeout(pWinCtx->hWndNextInChain, msg, wParam, lParam, 0,
314 VBOX_CLIPBOARD_CBCHAIN_TIMEOUT_MS, &dwResult);
315 if (!lresultRc)
316 lresultRc = dwResult;
317 }
318
319 LogFlowFunc(("lresultRc=%ld\n", lresultRc));
320 return lresultRc;
321}
322
323/**
324 * Converts a (registered or standard) Windows clipboard format to a VBox clipboard format.
325 *
326 * @returns Converted VBox clipboard format, or VBOX_SHARED_CLIPBOARD_FMT_NONE if not found.
327 * @param uFormat Windows clipboard format to convert.
328 */
329VBOXCLIPBOARDFORMAT VBoxClipboardWinClipboardFormatToVBox(UINT uFormat)
330{
331 /* Insert the requested clipboard format data into the clipboard. */
332 VBOXCLIPBOARDFORMAT vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_NONE;
333
334 switch (uFormat)
335 {
336 case CF_UNICODETEXT:
337 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
338 break;
339
340 case CF_DIB:
341 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
342 break;
343
344#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
345 /* CF_HDROP handles file system entries which are locally present
346 * on source for transferring to the target.
347 *
348 * This does *not* invoke any IDataObject / IStream implementations! */
349 case CF_HDROP:
350 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_URI_LIST;
351 break;
352#endif
353
354 default:
355 if (uFormat >= 0xC000) /** Formats registered with RegisterClipboardFormat() start at this index. */
356 {
357 TCHAR szFormatName[256]; /** @todo r=andy Do we need Unicode support here as well? */
358 int cActual = GetClipboardFormatName(uFormat, szFormatName, sizeof(szFormatName) / sizeof(TCHAR));
359 if (cActual)
360 {
361 LogFlowFunc(("uFormat=%u -> szFormatName=%s\n", uFormat, szFormatName));
362
363 if (RTStrCmp(szFormatName, VBOX_CLIPBOARD_WIN_REGFMT_HTML) == 0)
364 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_HTML;
365#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
366 /* These types invoke our IDataObject / IStream implementations. */
367 else if ( (RTStrCmp(szFormatName, CFSTR_FILEDESCRIPTORA) == 0)
368 || (RTStrCmp(szFormatName, CFSTR_FILECONTENTS) == 0))
369 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_URI_LIST;
370 /** @todo Do we need to handle CFSTR_FILEDESCRIPTORW here as well? */
371#endif
372 }
373 }
374 break;
375 }
376
377 LogFlowFunc(("uFormat=%u -> vboxFormat=0x%x\n", uFormat, vboxFormat));
378 return vboxFormat;
379}
380
381/**
382 * Retrieves all supported clipboard formats of a specific clipboard.
383 *
384 * @returns VBox status code.
385 * @param pCtx Windows clipboard context to retrieve formats for.
386 * @param pfFormats Where to store the retrieved formats of type VBOX_SHARED_CLIPBOARD_FMT_ (bitmask).
387 */
388int VBoxClipboardWinGetFormats(PVBOXCLIPBOARDWINCTX pCtx, PVBOXCLIPBOARDFORMATS pfFormats)
389{
390 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
391 AssertPtrReturn(pfFormats, VERR_INVALID_POINTER);
392
393 VBOXCLIPBOARDFORMATS fFormats = VBOX_SHARED_CLIPBOARD_FMT_NONE;
394
395 /* Query list of available formats and report to host. */
396 int rc = VBoxClipboardWinOpen(pCtx->hWnd);
397 if (RT_SUCCESS(rc))
398 {
399 UINT uCurFormat = 0; /* Must be set to zero for EnumClipboardFormats(). */
400 while ((uCurFormat = EnumClipboardFormats(uCurFormat)) != 0)
401 fFormats |= VBoxClipboardWinClipboardFormatToVBox(uCurFormat);
402
403 int rc2 = VBoxClipboardWinClose();
404 AssertRC(rc2);
405 }
406
407 if (RT_FAILURE(rc))
408 {
409 LogFunc(("Failed with rc=%Rrc\n", rc));
410 }
411 else
412 {
413 LogFlowFunc(("fFormats=0x%08X\n", fFormats));
414 *pfFormats = fFormats;
415 }
416
417 return rc;
418}
419
420/**
421 * Extracts a field value from CF_HTML data.
422 *
423 * @returns VBox status code.
424 * @param pszSrc source in CF_HTML format.
425 * @param pszOption Name of CF_HTML field.
426 * @param puValue Where to return extracted value of CF_HTML field.
427 */
428int VBoxClipboardWinGetCFHTMLHeaderValue(const char *pszSrc, const char *pszOption, uint32_t *puValue)
429{
430 AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
431 AssertPtrReturn(pszOption, VERR_INVALID_POINTER);
432
433 int rc = VERR_INVALID_PARAMETER;
434
435 const char *pszOptionValue = RTStrStr(pszSrc, pszOption);
436 if (pszOptionValue)
437 {
438 size_t cchOption = strlen(pszOption);
439 Assert(cchOption);
440
441 rc = RTStrToUInt32Ex(pszOptionValue + cchOption, NULL, 10, puValue);
442 }
443 return rc;
444}
445
446/**
447 * Check that the source string contains CF_HTML struct.
448 *
449 * @returns @c true if the @a pszSource string is in CF_HTML format.
450 * @param pszSource Source string to check.
451 */
452bool VBoxClipboardWinIsCFHTML(const char *pszSource)
453{
454 return RTStrStr(pszSource, "Version:") != NULL
455 && RTStrStr(pszSource, "StartHTML:") != NULL;
456}
457
458/**
459 * Converts clipboard data from CF_HTML format to MIME clipboard format.
460 *
461 * Returns allocated buffer that contains html converted to text/html mime type
462 *
463 * @returns VBox status code.
464 * @param pszSource The input.
465 * @param cch The length of the input.
466 * @param ppszOutput Where to return the result. Free using RTMemFree.
467 * @param pcbOutput Where to the return length of the result (bytes/chars).
468 */
469int VBoxClipboardWinConvertCFHTMLToMIME(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pcbOutput)
470{
471 Assert(pszSource);
472 Assert(cch);
473 Assert(ppszOutput);
474 Assert(pcbOutput);
475
476 uint32_t offStart;
477 int rc = VBoxClipboardWinGetCFHTMLHeaderValue(pszSource, "StartFragment:", &offStart);
478 if (RT_SUCCESS(rc))
479 {
480 uint32_t offEnd;
481 rc = VBoxClipboardWinGetCFHTMLHeaderValue(pszSource, "EndFragment:", &offEnd);
482 if (RT_SUCCESS(rc))
483 {
484 if ( offStart > 0
485 && offEnd > 0
486 && offEnd > offStart
487 && offEnd <= cch)
488 {
489 uint32_t cchSubStr = offEnd - offStart;
490 char *pszResult = (char *)RTMemAlloc(cchSubStr + 1);
491 if (pszResult)
492 {
493 rc = RTStrCopyEx(pszResult, cchSubStr + 1, pszSource + offStart, cchSubStr);
494 if (RT_SUCCESS(rc))
495 {
496 *ppszOutput = pszResult;
497 *pcbOutput = (uint32_t)(cchSubStr + 1);
498 rc = VINF_SUCCESS;
499 }
500 else
501 {
502 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
503 RTMemFree(pszResult);
504 }
505 }
506 else
507 {
508 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment\n"));
509 rc = VERR_NO_MEMORY;
510 }
511 }
512 else
513 {
514 LogRelFlowFunc(("Error: CF_HTML out of bounds - offStart=%#x offEnd=%#x cch=%#x\n", offStart, offEnd, cch));
515 rc = VERR_INVALID_PARAMETER;
516 }
517 }
518 else
519 {
520 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
521 rc = VERR_INVALID_PARAMETER;
522 }
523 }
524 else
525 {
526 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected StartFragment. rc = %Rrc\n", rc));
527 rc = VERR_INVALID_PARAMETER;
528 }
529
530 return rc;
531}
532
533/**
534 * Converts source UTF-8 MIME HTML clipboard data to UTF-8 CF_HTML format.
535 *
536 * This is just encapsulation work, slapping a header on the data.
537 *
538 * It allocates [..]
539 *
540 * Calculations:
541 * Header length = format Length + (2*(10 - 5('%010d'))('digits')) - 2('%s') = format length + 8
542 * EndHtml = Header length + fragment length
543 * StartHtml = 105(constant)
544 * StartFragment = 141(constant) may vary if the header html content will be extended
545 * EndFragment = Header length + fragment length - 38(ending length)
546 *
547 * @param pszSource Source buffer that contains utf-16 string in mime html format
548 * @param cb Size of source buffer in bytes
549 * @param ppszOutput Where to return the allocated output buffer to put converted UTF-8
550 * CF_HTML clipboard data. This function allocates memory for this.
551 * @param pcbOutput Where to return the size of allocated result buffer in bytes/chars, including zero terminator
552 *
553 * @note output buffer should be free using RTMemFree()
554 * @note Everything inside of fragment can be UTF8. Windows allows it. Everything in header should be Latin1.
555 */
556int VBoxClipboardWinConvertMIMEToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput)
557{
558 Assert(ppszOutput);
559 Assert(pcbOutput);
560 Assert(pszSource);
561 Assert(cb);
562
563 /* construct CF_HTML formatted string */
564 char *pszResult = NULL;
565 size_t cchFragment;
566 int rc = RTStrNLenEx(pszSource, cb, &cchFragment);
567 if (!RT_SUCCESS(rc))
568 {
569 LogRelFlowFunc(("Error: invalid source fragment. rc = %Rrc\n"));
570 return VERR_INVALID_PARAMETER;
571 }
572
573 /*
574 @StartHtml - pos before <html>
575 @EndHtml - whole size of text excluding ending zero char
576 @StartFragment - pos after <!--StartFragment-->
577 @EndFragment - pos before <!--EndFragment-->
578 @note: all values includes CR\LF inserted into text
579 Calculations:
580 Header length = format Length + (3*6('digits')) - 2('%s') = format length + 16 (control value - 183)
581 EndHtml = Header length + fragment length
582 StartHtml = 105(constant)
583 StartFragment = 143(constant)
584 EndFragment = Header length + fragment length - 40(ending length)
585 */
586 static const char s_szFormatSample[] =
587 /* 0: */ "Version:1.0\r\n"
588 /* 13: */ "StartHTML:000000101\r\n"
589 /* 34: */ "EndHTML:%0000009u\r\n" // END HTML = Header length + fragment length
590 /* 53: */ "StartFragment:000000137\r\n"
591 /* 78: */ "EndFragment:%0000009u\r\n"
592 /* 101: */ "<html>\r\n"
593 /* 109: */ "<body>\r\n"
594 /* 117: */ "<!--StartFragment-->"
595 /* 137: */ "%s"
596 /* 137+2: */ "<!--EndFragment-->\r\n"
597 /* 157+2: */ "</body>\r\n"
598 /* 166+2: */ "</html>\r\n";
599 /* 175+2: */
600 AssertCompile(sizeof(s_szFormatSample) == 175 + 2 + 1);
601
602 /* calculate parameters of CF_HTML header */
603 size_t cchHeader = sizeof(s_szFormatSample) - 1;
604 size_t offEndHtml = cchHeader + cchFragment;
605 size_t offEndFragment = cchHeader + cchFragment - 38; /* 175-137 = 38 */
606 pszResult = (char *)RTMemAlloc(offEndHtml + 1);
607 if (pszResult == NULL)
608 {
609 LogRelFlowFunc(("Error: Cannot allocate memory for result buffer. rc = %Rrc\n"));
610 return VERR_NO_MEMORY;
611 }
612
613 /* format result CF_HTML string */
614 size_t cchFormatted = RTStrPrintf(pszResult, offEndHtml + 1,
615 s_szFormatSample, offEndHtml, offEndFragment, pszSource);
616 Assert(offEndHtml == cchFormatted); NOREF(cchFormatted);
617
618#ifdef VBOX_STRICT
619 /* Control calculations. check consistency.*/
620 static const char s_szStartFragment[] = "<!--StartFragment-->";
621 static const char s_szEndFragment[] = "<!--EndFragment-->";
622
623 /* check 'StartFragment:' value */
624 const char *pszRealStartFragment = RTStrStr(pszResult, s_szStartFragment);
625 Assert(&pszRealStartFragment[sizeof(s_szStartFragment) - 1] - pszResult == 137);
626
627 /* check 'EndFragment:' value */
628 const char *pszRealEndFragment = RTStrStr(pszResult, s_szEndFragment);
629 Assert((size_t)(pszRealEndFragment - pszResult) == offEndFragment);
630#endif
631
632 *ppszOutput = pszResult;
633 *pcbOutput = (uint32_t)cchFormatted + 1;
634 Assert(*pcbOutput == cchFormatted + 1);
635
636 return VINF_SUCCESS;
637}
638
639/**
640 * Handles the WM_CHANGECBCHAIN code.
641 *
642 * @returns LRESULT
643 * @param pWinCtx Windows context to use.
644 * @param hWnd Window handle to use.
645 * @param msg Message ID to pass on.
646 * @param wParam wParam to pass on
647 * @param lParam lParam to pass on.
648 */
649LRESULT VBoxClipboardWinHandleWMChangeCBChain(PVBOXCLIPBOARDWINCTX pWinCtx,
650 HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
651{
652 LRESULT lresultRc = 0;
653
654 LogFlowFuncEnter();
655
656 if (VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI))
657 {
658 lresultRc = DefWindowProc(hWnd, msg, wParam, lParam);
659 }
660 else /* Old API */
661 {
662 HWND hwndRemoved = (HWND)wParam;
663 HWND hwndNext = (HWND)lParam;
664
665 if (hwndRemoved == pWinCtx->hWndNextInChain)
666 {
667 /* The window that was next to our in the chain is being removed.
668 * Relink to the new next window.
669 */
670 pWinCtx->hWndNextInChain = hwndNext;
671 }
672 else
673 {
674 if (pWinCtx->hWndNextInChain)
675 {
676 /* Pass the message further. */
677 DWORD_PTR dwResult;
678 lresultRc = SendMessageTimeout(pWinCtx->hWndNextInChain, WM_CHANGECBCHAIN, wParam, lParam, 0,
679 VBOX_CLIPBOARD_CBCHAIN_TIMEOUT_MS,
680 &dwResult);
681 if (!lresultRc)
682 lresultRc = (LRESULT)dwResult;
683 }
684 }
685 }
686
687 LogFlowFunc(("lresultRc=%ld\n", lresultRc));
688 return lresultRc;
689}
690
691/**
692 * Handles the WM_DESTROY code.
693 *
694 * @returns VBox status code.
695 * @param pWinCtx Windows context to use.
696 */
697int VBoxClipboardWinHandleWMDestroy(PVBOXCLIPBOARDWINCTX pWinCtx)
698{
699 LogFlowFuncEnter();
700
701 int rc = VINF_SUCCESS;
702
703 /* MS recommends to remove from Clipboard chain in this callback. */
704 VBoxClipboardWinChainRemove(pWinCtx);
705
706 if (pWinCtx->oldAPI.timerRefresh)
707 {
708 Assert(pWinCtx->hWnd);
709 KillTimer(pWinCtx->hWnd, 0);
710 }
711
712 LogFlowFuncLeaveRC(rc);
713 return rc;
714}
715
716/**
717 * Handles the WM_RENDERALLFORMATS message.
718 *
719 * @returns VBox status code.
720 * @param pWinCtx Windows context to use.
721 * @param hWnd Window handle to use.
722 */
723int VBoxClipboardWinHandleWMRenderAllFormats(PVBOXCLIPBOARDWINCTX pWinCtx, HWND hWnd)
724{
725 RT_NOREF(pWinCtx);
726
727 LogFlowFuncEnter();
728
729 /* Do nothing. The clipboard formats will be unavailable now, because the
730 * windows is to be destroyed and therefore the guest side becomes inactive.
731 */
732 int rc = VBoxClipboardWinOpen(hWnd);
733 if (RT_SUCCESS(rc))
734 {
735 VBoxClipboardWinClear();
736 VBoxClipboardWinClose();
737 }
738
739 LogFlowFuncLeaveRC(rc);
740 return rc;
741}
742
743/**
744 * Handles the WM_TIMER code, which is needed if we're running with the so-called "old" Windows clipboard API.
745 * Does nothing if we're running with the "new" Windows API.
746 *
747 * @returns VBox status code.
748 * @param pWinCtx Windows context to use.
749 */
750int VBoxClipboardWinHandleWMTimer(PVBOXCLIPBOARDWINCTX pWinCtx)
751{
752 int rc = VINF_SUCCESS;
753
754 if (!VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI)) /* Only run when using the "old" Windows API. */
755 {
756 LogFlowFuncEnter();
757
758 HWND hViewer = GetClipboardViewer();
759
760 /* Re-register ourselves in the clipboard chain if our last ping
761 * timed out or there seems to be no valid chain. */
762 if (!hViewer || pWinCtx->oldAPI.fCBChainPingInProcess)
763 {
764 VBoxClipboardWinChainRemove(pWinCtx);
765 VBoxClipboardWinChainAdd(pWinCtx);
766 }
767
768 /* Start a new ping by passing a dummy WM_CHANGECBCHAIN to be
769 * processed by ourselves to the chain. */
770 pWinCtx->oldAPI.fCBChainPingInProcess = TRUE;
771
772 hViewer = GetClipboardViewer();
773 if (hViewer)
774 SendMessageCallback(hViewer, WM_CHANGECBCHAIN, (WPARAM)pWinCtx->hWndNextInChain, (LPARAM)pWinCtx->hWndNextInChain,
775 VBoxClipboardWinChainPingProc, (ULONG_PTR)pWinCtx);
776 }
777
778 LogFlowFuncLeaveRC(rc);
779 return rc;
780}
781
782/**
783 * Announces a clipboard format to the Windows clipboard.
784 * The actual rendering (setting) of the clipboard data will be done later with a separate WM_RENDERFORMAT message.
785 *
786 * @returns VBox status code. VERR_NOT_SUPPORTED if the format is not supported / handled.
787 * @param pWinCtx Windows context to use.
788 * @param fFormats Clipboard format(s) to announce.
789 */
790int VBoxClipboardWinAnnounceFormats(PVBOXCLIPBOARDWINCTX pWinCtx, VBOXCLIPBOARDFORMATS fFormats)
791{
792 LogFunc(("fFormats=0x%x\n", fFormats));
793
794 HANDLE hClip = NULL;
795 UINT cfFormat = 0;
796
797 int rc = VINF_SUCCESS;
798
799 /** @todo r=andy Only one clipboard format can be set at once, at least on Windows. */
800 /** @todo Implement more flexible clipboard precedence for supported formats. */
801
802 if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
803 {
804 LogFunc(("CF_UNICODETEXT\n"));
805 hClip = SetClipboardData(CF_UNICODETEXT, NULL);
806 }
807 else if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
808 {
809 LogFunc(("CF_DIB\n"));
810 hClip = SetClipboardData(CF_DIB, NULL);
811 }
812 else if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
813 {
814 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_HTML\n"));
815 cfFormat = RegisterClipboardFormat(VBOX_CLIPBOARD_WIN_REGFMT_HTML);
816 if (cfFormat != 0)
817 hClip = SetClipboardData(cfFormat, NULL);
818 }
819 else
820 {
821 LogRel(("Shared Clipboard: Unsupported format(s) (0x%x), skipping\n", fFormats));
822 rc = VERR_NOT_SUPPORTED;
823 }
824
825 if (RT_SUCCESS(rc))
826 {
827 pWinCtx->hWndClipboardOwnerUs = GetClipboardOwner();
828 }
829
830 LogFlowFuncLeaveRC(rc);
831 return rc;
832}
833
834#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
835/**
836 * Creates an URI transfer by announcing URI data (via IDataObject) to Windows.
837 *
838 * This creates the necessary IDataObject + IStream implementations and initiates the actual transfers required for getting
839 * the meta data. Whether or not the actual (file++) transfer(s) are happening is up to the user (at some point) later then.
840 *
841 * @returns VBox status code.
842 * @param pWinCtx Windows context to use.
843 * @param pURICtx URI context to use.
844 * @param pTransfer URI transfer to use.
845 */
846int VBoxClipboardWinURITransferCreate(PVBOXCLIPBOARDWINCTX pWinCtx, PSHAREDCLIPBOARDURITRANSFER pTransfer)
847{
848 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
849
850 LogFlowFuncEnter();
851
852 int rc;
853
854 AssertReturn(pTransfer->pvUser == NULL, VERR_WRONG_ORDER);
855
856 SharedClipboardWinURITransferCtx *pWinURITransferCtx = new SharedClipboardWinURITransferCtx();
857 if (pWinURITransferCtx)
858 {
859 pTransfer->pvUser = pWinURITransferCtx;
860 pTransfer->cbUser = sizeof(SharedClipboardWinURITransferCtx);
861
862 pWinURITransferCtx->pDataObj = new VBoxClipboardWinDataObject(pTransfer);
863 if (pWinURITransferCtx->pDataObj)
864 {
865 rc = pWinURITransferCtx->pDataObj->Init();
866 if (RT_SUCCESS(rc))
867 {
868 VBoxClipboardWinClose();
869 /* Note: Clipboard must be closed first before calling OleSetClipboard(). */
870
871 /** @todo There is a potential race between VBoxClipboardWinClose() and OleSetClipboard(),
872 * where another application could own the clipboard (open), and thus the call to
873 * OleSetClipboard() will fail. Needs (better) fixing. */
874 for (unsigned uTries = 0; uTries < 3; uTries++)
875 {
876 HRESULT hr = OleSetClipboard(pWinURITransferCtx->pDataObj);
877 if (SUCCEEDED(hr))
878 {
879 /*
880 * Calling OleSetClipboard() changed the clipboard owner, which in turn will let us receive
881 * a WM_CLIPBOARDUPDATE message. To not confuse ourselves with our own clipboard owner changes,
882 * save a new window handle and deal with it in WM_CLIPBOARDUPDATE.
883 */
884 pWinCtx->hWndClipboardOwnerUs = GetClipboardOwner();
885 break;
886 }
887 else
888 {
889 rc = VERR_ACCESS_DENIED; /** @todo Fudge; fix this. */
890 LogRel(("Shared Clipboard: Failed with %Rhrc when setting data object to clipboard\n", hr));
891 RTThreadSleep(100); /* Wait a bit. */
892 }
893 }
894 }
895 }
896 else
897 rc = VERR_NO_MEMORY;
898 }
899 else
900 rc = VERR_NO_MEMORY;
901
902 LogFlowFuncLeaveRC(rc);
903 return rc;
904}
905
906/**
907 * Destroys implementation-specific data for an URI transfer.
908 *
909 * @param pWinCtx Windows context to use.
910 * @param pTransfer URI transfer to create implementation-specific data for.
911 */
912void VBoxClipboardWinURITransferDestroy(PVBOXCLIPBOARDWINCTX pWinCtx, PSHAREDCLIPBOARDURITRANSFER pTransfer)
913{
914 RT_NOREF(pWinCtx);
915
916 if (!pTransfer)
917 return;
918
919 LogFlowFuncEnter();
920
921 if (pTransfer->pvUser)
922 {
923 Assert(pTransfer->cbUser == sizeof(SharedClipboardWinURITransferCtx));
924 SharedClipboardWinURITransferCtx *pWinURITransferCtx = (SharedClipboardWinURITransferCtx *)pTransfer->pvUser;
925 Assert(pWinURITransferCtx);
926
927 if (pWinURITransferCtx->pDataObj)
928 {
929 delete pWinURITransferCtx->pDataObj;
930 pWinURITransferCtx->pDataObj = NULL;
931 }
932
933 delete pWinURITransferCtx;
934
935 pTransfer->pvUser = NULL;
936 pTransfer->cbUser = 0;
937 }
938}
939
940/**
941 * Converts a DROPFILES (HDROP) structure to a string list, separated by \r\n.
942 * Does not do any locking on the input data.
943 *
944 * @returns VBox status code.
945 * @param pDropFiles Pointer to DROPFILES structure to convert.
946 * @param papszList Where to store the allocated string list.
947 * @param pcbList Where to store the size (in bytes) of the allocated string list.
948 */
949int VBoxClipboardWinDropFilesToStringList(DROPFILES *pDropFiles, char **papszList, uint32_t *pcbList)
950{
951 AssertPtrReturn(pDropFiles, VERR_INVALID_POINTER);
952 AssertPtrReturn(papszList, VERR_INVALID_POINTER);
953 AssertPtrReturn(pcbList, VERR_INVALID_POINTER);
954
955 /* Do we need to do Unicode stuff? */
956 const bool fUnicode = RT_BOOL(pDropFiles->fWide);
957
958 /* Get the offset of the file list. */
959 Assert(pDropFiles->pFiles >= sizeof(DROPFILES));
960
961 /* Note: This is *not* pDropFiles->pFiles! DragQueryFile only
962 * will work with the plain storage medium pointer! */
963 HDROP hDrop = (HDROP)(pDropFiles);
964
965 int rc = VINF_SUCCESS;
966
967 /* First, get the file count. */
968 /** @todo Does this work on Windows 2000 / NT4? */
969 char *pszFiles = NULL;
970 uint32_t cchFiles = 0;
971 UINT cFiles = DragQueryFile(hDrop, UINT32_MAX /* iFile */, NULL /* lpszFile */, 0 /* cchFile */);
972
973 LogFlowFunc(("Got %RU16 file(s), fUnicode=%RTbool\n", cFiles, fUnicode));
974
975 for (UINT i = 0; i < cFiles; i++)
976 {
977 UINT cchFile = DragQueryFile(hDrop, i /* File index */, NULL /* Query size first */, 0 /* cchFile */);
978 Assert(cchFile);
979
980 if (RT_FAILURE(rc))
981 break;
982
983 char *pszFileUtf8 = NULL; /* UTF-8 version. */
984 UINT cchFileUtf8 = 0;
985 if (fUnicode)
986 {
987 /* Allocate enough space (including terminator). */
988 WCHAR *pwszFile = (WCHAR *)RTMemAlloc((cchFile + 1) * sizeof(WCHAR));
989 if (pwszFile)
990 {
991 const UINT cwcFileUtf16 = DragQueryFileW(hDrop, i /* File index */,
992 pwszFile, cchFile + 1 /* Include terminator */);
993
994 AssertMsg(cwcFileUtf16 == cchFile, ("cchFileUtf16 (%RU16) does not match cchFile (%RU16)\n",
995 cwcFileUtf16, cchFile));
996 RT_NOREF(cwcFileUtf16);
997
998 rc = RTUtf16ToUtf8(pwszFile, &pszFileUtf8);
999 if (RT_SUCCESS(rc))
1000 {
1001 cchFileUtf8 = (UINT)strlen(pszFileUtf8);
1002 Assert(cchFileUtf8);
1003 }
1004
1005 RTMemFree(pwszFile);
1006 }
1007 else
1008 rc = VERR_NO_MEMORY;
1009 }
1010 else /* ANSI */
1011 {
1012 /* Allocate enough space (including terminator). */
1013 pszFileUtf8 = (char *)RTMemAlloc((cchFile + 1) * sizeof(char));
1014 if (pszFileUtf8)
1015 {
1016 cchFileUtf8 = DragQueryFileA(hDrop, i /* File index */,
1017 pszFileUtf8, cchFile + 1 /* Include terminator */);
1018
1019 AssertMsg(cchFileUtf8 == cchFile, ("cchFileUtf8 (%RU16) does not match cchFile (%RU16)\n",
1020 cchFileUtf8, cchFile));
1021 }
1022 else
1023 rc = VERR_NO_MEMORY;
1024 }
1025
1026 if (RT_SUCCESS(rc))
1027 {
1028 LogFlowFunc(("\tFile: %s (cchFile=%RU16)\n", pszFileUtf8, cchFileUtf8));
1029
1030 LogRel(("Shared Clipboard: Adding guest file '%s'\n", pszFileUtf8));
1031
1032 rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, pszFileUtf8, strlen(pszFileUtf8));
1033 cchFiles += (uint32_t)strlen(pszFileUtf8);
1034 }
1035
1036 if (pszFileUtf8)
1037 RTStrFree(pszFileUtf8);
1038
1039 if (RT_FAILURE(rc))
1040 {
1041 LogFunc(("Error handling file entry #%u, rc=%Rrc\n", i, rc));
1042 break;
1043 }
1044
1045 /* Add separation between filenames.
1046 * Note: Also do this for the last element of the list. */
1047 rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, "\r\n", 2 /* Bytes */);
1048 if (RT_SUCCESS(rc))
1049 cchFiles += 2; /* Include \r\n */
1050 }
1051
1052 if (RT_SUCCESS(rc))
1053 {
1054 cchFiles += 1; /* Add string termination. */
1055 uint32_t cbFiles = cchFiles * sizeof(char); /* UTF-8. */
1056
1057 LogFlowFunc(("cFiles=%u, cchFiles=%RU32, cbFiles=%RU32, pszFiles=0x%p\n",
1058 cFiles, cchFiles, cbFiles, pszFiles));
1059
1060 *papszList = pszFiles;
1061 *pcbList = cbFiles;
1062 }
1063 else
1064 {
1065 if (pszFiles)
1066 RTStrFree(pszFiles);
1067 }
1068
1069 LogFlowFuncLeaveRC(rc);
1070 return rc;
1071}
1072#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
1073
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