VirtualBox

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

最後變更 在這個檔案從80444是 80444,由 vboxsync 提交於 6 年 前

Shared Clipboard/URI: Added protocol versioning support plus enhanced versions of existing commands (to also provide context IDs, among other stuff). So far only the host service(s) and the Windows guest is using the new(er) protocol.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 34.1 KB
 
1/* $Id: clipboard-win.cpp 80444 2019-08-27 17:47:44Z 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.alldomusa.eu.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 pFormats Where to store the retrieved formats.
387 */
388int VBoxClipboardWinGetFormats(PVBOXCLIPBOARDWINCTX pCtx, PSHAREDCLIPBOARDFORMATDATA pFormats)
389{
390 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
391 AssertPtrReturn(pFormats, 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
415 pFormats->uFormats = fFormats;
416 pFormats->fFlags = 0; /** @todo Handle flags. */
417 }
418
419 return rc;
420}
421
422/**
423 * Extracts a field value from CF_HTML data.
424 *
425 * @returns VBox status code.
426 * @param pszSrc source in CF_HTML format.
427 * @param pszOption Name of CF_HTML field.
428 * @param puValue Where to return extracted value of CF_HTML field.
429 */
430int VBoxClipboardWinGetCFHTMLHeaderValue(const char *pszSrc, const char *pszOption, uint32_t *puValue)
431{
432 AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
433 AssertPtrReturn(pszOption, VERR_INVALID_POINTER);
434
435 int rc = VERR_INVALID_PARAMETER;
436
437 const char *pszOptionValue = RTStrStr(pszSrc, pszOption);
438 if (pszOptionValue)
439 {
440 size_t cchOption = strlen(pszOption);
441 Assert(cchOption);
442
443 rc = RTStrToUInt32Ex(pszOptionValue + cchOption, NULL, 10, puValue);
444 }
445 return rc;
446}
447
448/**
449 * Check that the source string contains CF_HTML struct.
450 *
451 * @returns @c true if the @a pszSource string is in CF_HTML format.
452 * @param pszSource Source string to check.
453 */
454bool VBoxClipboardWinIsCFHTML(const char *pszSource)
455{
456 return RTStrStr(pszSource, "Version:") != NULL
457 && RTStrStr(pszSource, "StartHTML:") != NULL;
458}
459
460/**
461 * Converts clipboard data from CF_HTML format to MIME clipboard format.
462 *
463 * Returns allocated buffer that contains html converted to text/html mime type
464 *
465 * @returns VBox status code.
466 * @param pszSource The input.
467 * @param cch The length of the input.
468 * @param ppszOutput Where to return the result. Free using RTMemFree.
469 * @param pcbOutput Where to the return length of the result (bytes/chars).
470 */
471int VBoxClipboardWinConvertCFHTMLToMIME(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pcbOutput)
472{
473 Assert(pszSource);
474 Assert(cch);
475 Assert(ppszOutput);
476 Assert(pcbOutput);
477
478 uint32_t offStart;
479 int rc = VBoxClipboardWinGetCFHTMLHeaderValue(pszSource, "StartFragment:", &offStart);
480 if (RT_SUCCESS(rc))
481 {
482 uint32_t offEnd;
483 rc = VBoxClipboardWinGetCFHTMLHeaderValue(pszSource, "EndFragment:", &offEnd);
484 if (RT_SUCCESS(rc))
485 {
486 if ( offStart > 0
487 && offEnd > 0
488 && offEnd > offStart
489 && offEnd <= cch)
490 {
491 uint32_t cchSubStr = offEnd - offStart;
492 char *pszResult = (char *)RTMemAlloc(cchSubStr + 1);
493 if (pszResult)
494 {
495 rc = RTStrCopyEx(pszResult, cchSubStr + 1, pszSource + offStart, cchSubStr);
496 if (RT_SUCCESS(rc))
497 {
498 *ppszOutput = pszResult;
499 *pcbOutput = (uint32_t)(cchSubStr + 1);
500 rc = VINF_SUCCESS;
501 }
502 else
503 {
504 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
505 RTMemFree(pszResult);
506 }
507 }
508 else
509 {
510 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment\n"));
511 rc = VERR_NO_MEMORY;
512 }
513 }
514 else
515 {
516 LogRelFlowFunc(("Error: CF_HTML out of bounds - offStart=%#x offEnd=%#x cch=%#x\n", offStart, offEnd, cch));
517 rc = VERR_INVALID_PARAMETER;
518 }
519 }
520 else
521 {
522 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
523 rc = VERR_INVALID_PARAMETER;
524 }
525 }
526 else
527 {
528 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected StartFragment. rc = %Rrc\n", rc));
529 rc = VERR_INVALID_PARAMETER;
530 }
531
532 return rc;
533}
534
535/**
536 * Converts source UTF-8 MIME HTML clipboard data to UTF-8 CF_HTML format.
537 *
538 * This is just encapsulation work, slapping a header on the data.
539 *
540 * It allocates [..]
541 *
542 * Calculations:
543 * Header length = format Length + (2*(10 - 5('%010d'))('digits')) - 2('%s') = format length + 8
544 * EndHtml = Header length + fragment length
545 * StartHtml = 105(constant)
546 * StartFragment = 141(constant) may vary if the header html content will be extended
547 * EndFragment = Header length + fragment length - 38(ending length)
548 *
549 * @param pszSource Source buffer that contains utf-16 string in mime html format
550 * @param cb Size of source buffer in bytes
551 * @param ppszOutput Where to return the allocated output buffer to put converted UTF-8
552 * CF_HTML clipboard data. This function allocates memory for this.
553 * @param pcbOutput Where to return the size of allocated result buffer in bytes/chars, including zero terminator
554 *
555 * @note output buffer should be free using RTMemFree()
556 * @note Everything inside of fragment can be UTF8. Windows allows it. Everything in header should be Latin1.
557 */
558int VBoxClipboardWinConvertMIMEToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput)
559{
560 Assert(ppszOutput);
561 Assert(pcbOutput);
562 Assert(pszSource);
563 Assert(cb);
564
565 /* construct CF_HTML formatted string */
566 char *pszResult = NULL;
567 size_t cchFragment;
568 int rc = RTStrNLenEx(pszSource, cb, &cchFragment);
569 if (!RT_SUCCESS(rc))
570 {
571 LogRelFlowFunc(("Error: invalid source fragment. rc = %Rrc\n"));
572 return VERR_INVALID_PARAMETER;
573 }
574
575 /*
576 @StartHtml - pos before <html>
577 @EndHtml - whole size of text excluding ending zero char
578 @StartFragment - pos after <!--StartFragment-->
579 @EndFragment - pos before <!--EndFragment-->
580 @note: all values includes CR\LF inserted into text
581 Calculations:
582 Header length = format Length + (3*6('digits')) - 2('%s') = format length + 16 (control value - 183)
583 EndHtml = Header length + fragment length
584 StartHtml = 105(constant)
585 StartFragment = 143(constant)
586 EndFragment = Header length + fragment length - 40(ending length)
587 */
588 static const char s_szFormatSample[] =
589 /* 0: */ "Version:1.0\r\n"
590 /* 13: */ "StartHTML:000000101\r\n"
591 /* 34: */ "EndHTML:%0000009u\r\n" // END HTML = Header length + fragment length
592 /* 53: */ "StartFragment:000000137\r\n"
593 /* 78: */ "EndFragment:%0000009u\r\n"
594 /* 101: */ "<html>\r\n"
595 /* 109: */ "<body>\r\n"
596 /* 117: */ "<!--StartFragment-->"
597 /* 137: */ "%s"
598 /* 137+2: */ "<!--EndFragment-->\r\n"
599 /* 157+2: */ "</body>\r\n"
600 /* 166+2: */ "</html>\r\n";
601 /* 175+2: */
602 AssertCompile(sizeof(s_szFormatSample) == 175 + 2 + 1);
603
604 /* calculate parameters of CF_HTML header */
605 size_t cchHeader = sizeof(s_szFormatSample) - 1;
606 size_t offEndHtml = cchHeader + cchFragment;
607 size_t offEndFragment = cchHeader + cchFragment - 38; /* 175-137 = 38 */
608 pszResult = (char *)RTMemAlloc(offEndHtml + 1);
609 if (pszResult == NULL)
610 {
611 LogRelFlowFunc(("Error: Cannot allocate memory for result buffer. rc = %Rrc\n"));
612 return VERR_NO_MEMORY;
613 }
614
615 /* format result CF_HTML string */
616 size_t cchFormatted = RTStrPrintf(pszResult, offEndHtml + 1,
617 s_szFormatSample, offEndHtml, offEndFragment, pszSource);
618 Assert(offEndHtml == cchFormatted); NOREF(cchFormatted);
619
620#ifdef VBOX_STRICT
621 /* Control calculations. check consistency.*/
622 static const char s_szStartFragment[] = "<!--StartFragment-->";
623 static const char s_szEndFragment[] = "<!--EndFragment-->";
624
625 /* check 'StartFragment:' value */
626 const char *pszRealStartFragment = RTStrStr(pszResult, s_szStartFragment);
627 Assert(&pszRealStartFragment[sizeof(s_szStartFragment) - 1] - pszResult == 137);
628
629 /* check 'EndFragment:' value */
630 const char *pszRealEndFragment = RTStrStr(pszResult, s_szEndFragment);
631 Assert((size_t)(pszRealEndFragment - pszResult) == offEndFragment);
632#endif
633
634 *ppszOutput = pszResult;
635 *pcbOutput = (uint32_t)cchFormatted + 1;
636 Assert(*pcbOutput == cchFormatted + 1);
637
638 return VINF_SUCCESS;
639}
640
641/**
642 * Handles the WM_CHANGECBCHAIN code.
643 *
644 * @returns LRESULT
645 * @param pWinCtx Windows context to use.
646 * @param hWnd Window handle to use.
647 * @param msg Message ID to pass on.
648 * @param wParam wParam to pass on
649 * @param lParam lParam to pass on.
650 */
651LRESULT VBoxClipboardWinHandleWMChangeCBChain(PVBOXCLIPBOARDWINCTX pWinCtx,
652 HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
653{
654 LRESULT lresultRc = 0;
655
656 LogFlowFuncEnter();
657
658 if (VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI))
659 {
660 lresultRc = DefWindowProc(hWnd, msg, wParam, lParam);
661 }
662 else /* Old API */
663 {
664 HWND hwndRemoved = (HWND)wParam;
665 HWND hwndNext = (HWND)lParam;
666
667 if (hwndRemoved == pWinCtx->hWndNextInChain)
668 {
669 /* The window that was next to our in the chain is being removed.
670 * Relink to the new next window.
671 */
672 pWinCtx->hWndNextInChain = hwndNext;
673 }
674 else
675 {
676 if (pWinCtx->hWndNextInChain)
677 {
678 /* Pass the message further. */
679 DWORD_PTR dwResult;
680 lresultRc = SendMessageTimeout(pWinCtx->hWndNextInChain, WM_CHANGECBCHAIN, wParam, lParam, 0,
681 VBOX_CLIPBOARD_CBCHAIN_TIMEOUT_MS,
682 &dwResult);
683 if (!lresultRc)
684 lresultRc = (LRESULT)dwResult;
685 }
686 }
687 }
688
689 LogFlowFunc(("lresultRc=%ld\n", lresultRc));
690 return lresultRc;
691}
692
693/**
694 * Handles the WM_DESTROY code.
695 *
696 * @returns VBox status code.
697 * @param pWinCtx Windows context to use.
698 */
699int VBoxClipboardWinHandleWMDestroy(PVBOXCLIPBOARDWINCTX pWinCtx)
700{
701 LogFlowFuncEnter();
702
703 int rc = VINF_SUCCESS;
704
705 /* MS recommends to remove from Clipboard chain in this callback. */
706 VBoxClipboardWinChainRemove(pWinCtx);
707
708 if (pWinCtx->oldAPI.timerRefresh)
709 {
710 Assert(pWinCtx->hWnd);
711 KillTimer(pWinCtx->hWnd, 0);
712 }
713
714 LogFlowFuncLeaveRC(rc);
715 return rc;
716}
717
718/**
719 * Handles the WM_RENDERALLFORMATS message.
720 *
721 * @returns VBox status code.
722 * @param pWinCtx Windows context to use.
723 * @param hWnd Window handle to use.
724 */
725int VBoxClipboardWinHandleWMRenderAllFormats(PVBOXCLIPBOARDWINCTX pWinCtx, HWND hWnd)
726{
727 RT_NOREF(pWinCtx);
728
729 LogFlowFuncEnter();
730
731 /* Do nothing. The clipboard formats will be unavailable now, because the
732 * windows is to be destroyed and therefore the guest side becomes inactive.
733 */
734 int rc = VBoxClipboardWinOpen(hWnd);
735 if (RT_SUCCESS(rc))
736 {
737 VBoxClipboardWinClear();
738 VBoxClipboardWinClose();
739 }
740
741 LogFlowFuncLeaveRC(rc);
742 return rc;
743}
744
745/**
746 * Handles the WM_TIMER code, which is needed if we're running with the so-called "old" Windows clipboard API.
747 * Does nothing if we're running with the "new" Windows API.
748 *
749 * @returns VBox status code.
750 * @param pWinCtx Windows context to use.
751 */
752int VBoxClipboardWinHandleWMTimer(PVBOXCLIPBOARDWINCTX pWinCtx)
753{
754 int rc = VINF_SUCCESS;
755
756 if (!VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI)) /* Only run when using the "old" Windows API. */
757 {
758 LogFlowFuncEnter();
759
760 HWND hViewer = GetClipboardViewer();
761
762 /* Re-register ourselves in the clipboard chain if our last ping
763 * timed out or there seems to be no valid chain. */
764 if (!hViewer || pWinCtx->oldAPI.fCBChainPingInProcess)
765 {
766 VBoxClipboardWinChainRemove(pWinCtx);
767 VBoxClipboardWinChainAdd(pWinCtx);
768 }
769
770 /* Start a new ping by passing a dummy WM_CHANGECBCHAIN to be
771 * processed by ourselves to the chain. */
772 pWinCtx->oldAPI.fCBChainPingInProcess = TRUE;
773
774 hViewer = GetClipboardViewer();
775 if (hViewer)
776 SendMessageCallback(hViewer, WM_CHANGECBCHAIN, (WPARAM)pWinCtx->hWndNextInChain, (LPARAM)pWinCtx->hWndNextInChain,
777 VBoxClipboardWinChainPingProc, (ULONG_PTR)pWinCtx);
778 }
779
780 LogFlowFuncLeaveRC(rc);
781 return rc;
782}
783
784/**
785 * Announces a clipboard format to the Windows clipboard.
786 * The actual rendering (setting) of the clipboard data will be done later with a separate WM_RENDERFORMAT message.
787 *
788 * @returns VBox status code. VERR_NOT_SUPPORTED if the format is not supported / handled.
789 * @param pWinCtx Windows context to use.
790 * @param fFormats Clipboard format(s) to announce.
791 */
792int VBoxClipboardWinAnnounceFormats(PVBOXCLIPBOARDWINCTX pWinCtx, VBOXCLIPBOARDFORMATS fFormats)
793{
794 LogFunc(("fFormats=0x%x\n", fFormats));
795
796 HANDLE hClip = NULL;
797 UINT cfFormat = 0;
798
799 int rc = VINF_SUCCESS;
800
801 /** @todo r=andy Only one clipboard format can be set at once, at least on Windows. */
802 /** @todo Implement more flexible clipboard precedence for supported formats. */
803
804 if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
805 {
806 LogFunc(("CF_UNICODETEXT\n"));
807 hClip = SetClipboardData(CF_UNICODETEXT, NULL);
808 }
809 else if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
810 {
811 LogFunc(("CF_DIB\n"));
812 hClip = SetClipboardData(CF_DIB, NULL);
813 }
814 else if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
815 {
816 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_HTML\n"));
817 cfFormat = RegisterClipboardFormat(VBOX_CLIPBOARD_WIN_REGFMT_HTML);
818 if (cfFormat != 0)
819 hClip = SetClipboardData(cfFormat, NULL);
820 }
821 else
822 {
823 LogRel(("Shared Clipboard: Unsupported format(s) (0x%x), skipping\n", fFormats));
824 rc = VERR_NOT_SUPPORTED;
825 }
826
827 if (RT_SUCCESS(rc))
828 {
829 pWinCtx->hWndClipboardOwnerUs = GetClipboardOwner();
830 }
831
832 LogFlowFuncLeaveRC(rc);
833 return rc;
834}
835
836#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
837/**
838 * Creates an URI transfer by announcing URI data (via IDataObject) to Windows.
839 *
840 * This creates the necessary IDataObject + IStream implementations and initiates the actual transfers required for getting
841 * the meta data. Whether or not the actual (file++) transfer(s) are happening is up to the user (at some point) later then.
842 *
843 * @returns VBox status code.
844 * @param pWinCtx Windows context to use.
845 * @param pURICtx URI context to use.
846 * @param pTransfer URI transfer to use.
847 */
848int VBoxClipboardWinURITransferCreate(PVBOXCLIPBOARDWINCTX pWinCtx, PSHAREDCLIPBOARDURITRANSFER pTransfer)
849{
850 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
851
852 LogFlowFuncEnter();
853
854 int rc;
855
856 AssertReturn(pTransfer->pvUser == NULL, VERR_WRONG_ORDER);
857
858 SharedClipboardWinURITransferCtx *pWinURITransferCtx = new SharedClipboardWinURITransferCtx();
859 if (pWinURITransferCtx)
860 {
861 pTransfer->pvUser = pWinURITransferCtx;
862 pTransfer->cbUser = sizeof(SharedClipboardWinURITransferCtx);
863
864 pWinURITransferCtx->pDataObj = new VBoxClipboardWinDataObject(pTransfer);
865 if (pWinURITransferCtx->pDataObj)
866 {
867 rc = pWinURITransferCtx->pDataObj->Init();
868 if (RT_SUCCESS(rc))
869 {
870 VBoxClipboardWinClose();
871 /* Note: Clipboard must be closed first before calling OleSetClipboard(). */
872
873 /** @todo There is a potential race between VBoxClipboardWinClose() and OleSetClipboard(),
874 * where another application could own the clipboard (open), and thus the call to
875 * OleSetClipboard() will fail. Needs (better) fixing. */
876 for (unsigned uTries = 0; uTries < 3; uTries++)
877 {
878 HRESULT hr = OleSetClipboard(pWinURITransferCtx->pDataObj);
879 if (SUCCEEDED(hr))
880 {
881 /*
882 * Calling OleSetClipboard() changed the clipboard owner, which in turn will let us receive
883 * a WM_CLIPBOARDUPDATE message. To not confuse ourselves with our own clipboard owner changes,
884 * save a new window handle and deal with it in WM_CLIPBOARDUPDATE.
885 */
886 pWinCtx->hWndClipboardOwnerUs = GetClipboardOwner();
887 break;
888 }
889 else
890 {
891 rc = VERR_ACCESS_DENIED; /** @todo Fudge; fix this. */
892 LogRel(("Shared Clipboard: Failed with %Rhrc when setting data object to clipboard\n", hr));
893 RTThreadSleep(100); /* Wait a bit. */
894 }
895 }
896 }
897 }
898 else
899 rc = VERR_NO_MEMORY;
900 }
901 else
902 rc = VERR_NO_MEMORY;
903
904 LogFlowFuncLeaveRC(rc);
905 return rc;
906}
907
908/**
909 * Destroys implementation-specific data for an URI transfer.
910 *
911 * @param pWinCtx Windows context to use.
912 * @param pTransfer URI transfer to create implementation-specific data for.
913 */
914void VBoxClipboardWinURITransferDestroy(PVBOXCLIPBOARDWINCTX pWinCtx, PSHAREDCLIPBOARDURITRANSFER pTransfer)
915{
916 RT_NOREF(pWinCtx);
917
918 if (!pTransfer)
919 return;
920
921 LogFlowFuncEnter();
922
923 if (pTransfer->pvUser)
924 {
925 Assert(pTransfer->cbUser == sizeof(SharedClipboardWinURITransferCtx));
926 SharedClipboardWinURITransferCtx *pWinURITransferCtx = (SharedClipboardWinURITransferCtx *)pTransfer->pvUser;
927 Assert(pWinURITransferCtx);
928
929 if (pWinURITransferCtx->pDataObj)
930 {
931 delete pWinURITransferCtx->pDataObj;
932 pWinURITransferCtx->pDataObj = NULL;
933 }
934
935 delete pWinURITransferCtx;
936
937 pTransfer->pvUser = NULL;
938 pTransfer->cbUser = 0;
939 }
940}
941
942/**
943 * Converts a DROPFILES (HDROP) structure to a string list, separated by \r\n.
944 * Does not do any locking on the input data.
945 *
946 * @returns VBox status code.
947 * @param pDropFiles Pointer to DROPFILES structure to convert.
948 * @param papszList Where to store the allocated string list.
949 * @param pcbList Where to store the size (in bytes) of the allocated string list.
950 */
951int VBoxClipboardWinDropFilesToStringList(DROPFILES *pDropFiles, char **papszList, uint32_t *pcbList)
952{
953 AssertPtrReturn(pDropFiles, VERR_INVALID_POINTER);
954 AssertPtrReturn(papszList, VERR_INVALID_POINTER);
955 AssertPtrReturn(pcbList, VERR_INVALID_POINTER);
956
957 /* Do we need to do Unicode stuff? */
958 const bool fUnicode = RT_BOOL(pDropFiles->fWide);
959
960 /* Get the offset of the file list. */
961 Assert(pDropFiles->pFiles >= sizeof(DROPFILES));
962
963 /* Note: This is *not* pDropFiles->pFiles! DragQueryFile only
964 * will work with the plain storage medium pointer! */
965 HDROP hDrop = (HDROP)(pDropFiles);
966
967 int rc = VINF_SUCCESS;
968
969 /* First, get the file count. */
970 /** @todo Does this work on Windows 2000 / NT4? */
971 char *pszFiles = NULL;
972 uint32_t cchFiles = 0;
973 UINT cFiles = DragQueryFile(hDrop, UINT32_MAX /* iFile */, NULL /* lpszFile */, 0 /* cchFile */);
974
975 LogFlowFunc(("Got %RU16 file(s), fUnicode=%RTbool\n", cFiles, fUnicode));
976
977 for (UINT i = 0; i < cFiles; i++)
978 {
979 UINT cchFile = DragQueryFile(hDrop, i /* File index */, NULL /* Query size first */, 0 /* cchFile */);
980 Assert(cchFile);
981
982 if (RT_FAILURE(rc))
983 break;
984
985 char *pszFileUtf8 = NULL; /* UTF-8 version. */
986 UINT cchFileUtf8 = 0;
987 if (fUnicode)
988 {
989 /* Allocate enough space (including terminator). */
990 WCHAR *pwszFile = (WCHAR *)RTMemAlloc((cchFile + 1) * sizeof(WCHAR));
991 if (pwszFile)
992 {
993 const UINT cwcFileUtf16 = DragQueryFileW(hDrop, i /* File index */,
994 pwszFile, cchFile + 1 /* Include terminator */);
995
996 AssertMsg(cwcFileUtf16 == cchFile, ("cchFileUtf16 (%RU16) does not match cchFile (%RU16)\n",
997 cwcFileUtf16, cchFile));
998 RT_NOREF(cwcFileUtf16);
999
1000 rc = RTUtf16ToUtf8(pwszFile, &pszFileUtf8);
1001 if (RT_SUCCESS(rc))
1002 {
1003 cchFileUtf8 = (UINT)strlen(pszFileUtf8);
1004 Assert(cchFileUtf8);
1005 }
1006
1007 RTMemFree(pwszFile);
1008 }
1009 else
1010 rc = VERR_NO_MEMORY;
1011 }
1012 else /* ANSI */
1013 {
1014 /* Allocate enough space (including terminator). */
1015 pszFileUtf8 = (char *)RTMemAlloc((cchFile + 1) * sizeof(char));
1016 if (pszFileUtf8)
1017 {
1018 cchFileUtf8 = DragQueryFileA(hDrop, i /* File index */,
1019 pszFileUtf8, cchFile + 1 /* Include terminator */);
1020
1021 AssertMsg(cchFileUtf8 == cchFile, ("cchFileUtf8 (%RU16) does not match cchFile (%RU16)\n",
1022 cchFileUtf8, cchFile));
1023 }
1024 else
1025 rc = VERR_NO_MEMORY;
1026 }
1027
1028 if (RT_SUCCESS(rc))
1029 {
1030 LogFlowFunc(("\tFile: %s (cchFile=%RU16)\n", pszFileUtf8, cchFileUtf8));
1031
1032 LogRel(("Shared Clipboard: Adding guest file '%s'\n", pszFileUtf8));
1033
1034 rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, pszFileUtf8, strlen(pszFileUtf8));
1035 cchFiles += (uint32_t)strlen(pszFileUtf8);
1036 }
1037
1038 if (pszFileUtf8)
1039 RTStrFree(pszFileUtf8);
1040
1041 if (RT_FAILURE(rc))
1042 {
1043 LogFunc(("Error handling file entry #%u, rc=%Rrc\n", i, rc));
1044 break;
1045 }
1046
1047 /* Add separation between filenames.
1048 * Note: Also do this for the last element of the list. */
1049 rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, "\r\n", 2 /* Bytes */);
1050 if (RT_SUCCESS(rc))
1051 cchFiles += 2; /* Include \r\n */
1052 }
1053
1054 if (RT_SUCCESS(rc))
1055 {
1056 cchFiles += 1; /* Add string termination. */
1057 uint32_t cbFiles = cchFiles * sizeof(char); /* UTF-8. */
1058
1059 LogFlowFunc(("cFiles=%u, cchFiles=%RU32, cbFiles=%RU32, pszFiles=0x%p\n",
1060 cFiles, cchFiles, cbFiles, pszFiles));
1061
1062 *papszList = pszFiles;
1063 *pcbList = cbFiles;
1064 }
1065 else
1066 {
1067 if (pszFiles)
1068 RTStrFree(pszFiles);
1069 }
1070
1071 LogFlowFuncLeaveRC(rc);
1072 return rc;
1073}
1074#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
1075
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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