VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxClipboard-win.cpp@ 64573

Last change on this file since 64573 was 63549, checked in by vboxsync, 8 years ago

scm cleanups

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.7 KB
Line 
1/** @file
2 * Shared Clipboard: Win32 host.
3 */
4
5/*
6 * Copyright (C) 2006-2016 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17#include <iprt/win/windows.h>
18
19#include <VBox/HostServices/VBoxClipboardSvc.h>
20
21#include <iprt/alloc.h>
22#include <iprt/string.h>
23#include <iprt/asm.h>
24#include <iprt/assert.h>
25#include <iprt/thread.h>
26#include <iprt/ldr.h>
27#include <process.h>
28
29#include "VBoxClipboard.h"
30
31#define dprintf Log
32
33static char gachWindowClassName[] = "VBoxSharedClipboardClass";
34
35enum { CBCHAIN_TIMEOUT = 5000 /* ms */ };
36
37/* Dynamically load clipboard functions from User32.dll. */
38typedef BOOL WINAPI FNADDCLIPBOARDFORMATLISTENER(HWND);
39typedef FNADDCLIPBOARDFORMATLISTENER *PFNADDCLIPBOARDFORMATLISTENER;
40
41typedef BOOL WINAPI FNREMOVECLIPBOARDFORMATLISTENER(HWND);
42typedef FNREMOVECLIPBOARDFORMATLISTENER *PFNREMOVECLIPBOARDFORMATLISTENER;
43
44
45/*********************************************************************************************************************************
46* Internal Functions *
47*********************************************************************************************************************************/
48static int ConvertCFHtmlToMime(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pch);
49static int ConvertMimeToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput);
50static bool IsWindowsHTML(const char *source);
51
52
53#ifndef WM_CLIPBOARDUPDATE
54#define WM_CLIPBOARDUPDATE 0x031D
55#endif
56
57struct _VBOXCLIPBOARDCONTEXT
58{
59 HWND hwnd;
60 HWND hwndNextInChain;
61
62 UINT timerRefresh;
63
64 bool fCBChainPingInProcess;
65
66 RTTHREAD thread;
67
68 HANDLE hRenderEvent;
69
70 VBOXCLIPBOARDCLIENTDATA *pClient;
71
72 PFNADDCLIPBOARDFORMATLISTENER pfnAddClipboardFormatListener;
73 PFNREMOVECLIPBOARDFORMATLISTENER pfnRemoveClipboardFormatListener;
74
75};
76
77/* Only one client is supported. There seems to be no need for more clients. */
78static VBOXCLIPBOARDCONTEXT g_ctx;
79
80
81#ifdef LOG_ENABLED
82void vboxClipboardDump(const void *pv, size_t cb, uint32_t u32Format)
83{
84 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
85 {
86 Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT:\n"));
87 if (pv && cb)
88 Log(("%ls\n", pv));
89 else
90 Log(("%p %d\n", pv, cb));
91 }
92 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
93 dprintf(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
94 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
95 {
96 Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_HTML:\n"));
97 if (pv && cb)
98 {
99 Log(("%s\n", pv));
100
101 //size_t cb = RTStrNLen(pv, );
102 char *pszBuf = (char *)RTMemAllocZ(cb + 1);
103 RTStrCopy(pszBuf, cb + 1, (const char *)pv);
104 for (size_t off = 0; off < cb; ++off)
105 {
106 if (pszBuf[off] == '\n' || pszBuf[off] == '\r')
107 pszBuf[off] = ' ';
108 }
109
110 Log(("%s\n", pszBuf));
111 RTMemFree(pszBuf);
112 }
113 else
114 Log(("%p %d\n", pv, cb));
115 }
116 else
117 dprintf(("DUMP: invalid format %02X\n", u32Format));
118}
119#else /* !LOG_ENABLED */
120# define vboxClipboardDump(__pv, __cb, __format) do { NOREF(__pv); NOREF(__cb); NOREF(__format); } while (0)
121#endif /* !LOG_ENABLED */
122
123
124static void vboxClipboardInitNewAPI(VBOXCLIPBOARDCONTEXT *pCtx)
125{
126 RTLDRMOD hUser32 = NIL_RTLDRMOD;
127 int rc = RTLdrLoadSystem("User32.dll", /* fNoUnload = */ true, &hUser32);
128 if (RT_SUCCESS(rc))
129 {
130 rc = RTLdrGetSymbol(hUser32, "AddClipboardFormatListener", (void**)&pCtx->pfnAddClipboardFormatListener);
131 if (RT_SUCCESS(rc))
132 {
133 rc = RTLdrGetSymbol(hUser32, "RemoveClipboardFormatListener", (void**)&pCtx->pfnRemoveClipboardFormatListener);
134 }
135
136 RTLdrClose(hUser32);
137 }
138
139 if (RT_SUCCESS(rc))
140 {
141 Log(("New Clipboard API is enabled\n"));
142 }
143 else
144 {
145 pCtx->pfnAddClipboardFormatListener = NULL;
146 pCtx->pfnRemoveClipboardFormatListener = NULL;
147 Log(("New Clipboard API is not available. rc = %Rrc\n", rc));
148 }
149}
150
151static bool vboxClipboardIsNewAPI(VBOXCLIPBOARDCONTEXT *pCtx)
152{
153 return pCtx->pfnAddClipboardFormatListener != NULL;
154}
155
156
157static int vboxOpenClipboard(HWND hwnd)
158{
159 /* "OpenClipboard fails if another window has the clipboard open."
160 * So try a few times and wait up to 1 second.
161 */
162 BOOL fOpened = FALSE;
163
164 int i = 0;
165 for (;;)
166 {
167 if (OpenClipboard(hwnd))
168 {
169 fOpened = TRUE;
170 break;
171 }
172
173 if (i >= 10) /* sleep interval = [1..512] ms */
174 break;
175
176 RTThreadSleep(1 << i);
177 ++i;
178 }
179
180#ifdef LOG_ENABLED
181 if (i > 0)
182 LogFlowFunc(("%d times tried to open clipboard.\n", i + 1));
183#endif
184
185 int rc;
186 if (fOpened)
187 rc = VINF_SUCCESS;
188 else
189 {
190 const DWORD err = GetLastError();
191 LogFlowFunc(("error %d\n", err));
192 rc = RTErrConvertFromWin32(err);
193 }
194
195 return rc;
196}
197
198
199/** @todo Someone please explain the protocol wrt overflows... */
200static void vboxClipboardGetData (uint32_t u32Format, const void *pvSrc, uint32_t cbSrc,
201 void *pvDst, uint32_t cbDst, uint32_t *pcbActualDst)
202{
203 dprintf (("vboxClipboardGetData.\n"));
204
205 LogFlow(("vboxClipboardGetData cbSrc = %d, cbDst = %d\n", cbSrc, cbDst));
206
207 if ( u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML
208 && IsWindowsHTML((const char *)pvSrc))
209 {
210 /** @todo r=bird: Why the double conversion? */
211 char *pszBuf = NULL;
212 uint32_t cbBuf = 0;
213 int rc = ConvertCFHtmlToMime((const char*)pvSrc, cbSrc, &pszBuf, &cbBuf);
214 if (RT_SUCCESS(rc))
215 {
216 *pcbActualDst = cbBuf;
217 if (cbBuf > cbDst)
218 {
219 /* Do not copy data. The dst buffer is not enough. */
220 RTMemFree(pszBuf);
221 return;
222 }
223 memcpy(pvDst, pszBuf, cbBuf);
224 RTMemFree(pszBuf);
225 }
226 else
227 *pcbActualDst = 0;
228 }
229 else
230 {
231 *pcbActualDst = cbSrc;
232
233 if (cbSrc > cbDst)
234 {
235 /* Do not copy data. The dst buffer is not enough. */
236 return;
237 }
238
239 memcpy(pvDst, pvSrc, cbSrc);
240 }
241
242 vboxClipboardDump(pvDst, cbSrc, u32Format);
243
244 return;
245}
246
247static int vboxClipboardReadDataFromClient (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format)
248{
249 Assert(pCtx->pClient);
250 Assert(pCtx->hRenderEvent);
251 Assert(pCtx->pClient->data.pv == NULL && pCtx->pClient->data.cb == 0 && pCtx->pClient->data.u32Format == 0);
252
253 LogFlow(("vboxClipboardReadDataFromClient u32Format = %02X\n", u32Format));
254
255 ResetEvent (pCtx->hRenderEvent);
256
257 vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
258
259 DWORD ret = WaitForSingleObject(pCtx->hRenderEvent, INFINITE);
260 LogFlow(("vboxClipboardReadDataFromClient wait completed, ret 0x%08X, err %d\n",
261 ret, GetLastError())); NOREF(ret);
262
263 return VINF_SUCCESS;
264}
265
266static void vboxClipboardChanged (VBOXCLIPBOARDCONTEXT *pCtx)
267{
268 LogFlow(("vboxClipboardChanged\n"));
269
270 if (pCtx->pClient == NULL)
271 {
272 return;
273 }
274
275 /* Query list of available formats and report to host. */
276 int rc = vboxOpenClipboard(pCtx->hwnd);
277 if (RT_SUCCESS(rc))
278 {
279 uint32_t u32Formats = 0;
280
281 UINT format = 0;
282
283 while ((format = EnumClipboardFormats (format)) != 0)
284 {
285 LogFlow(("vboxClipboardChanged format %#x\n", format));
286 switch (format)
287 {
288 case CF_UNICODETEXT:
289 case CF_TEXT:
290 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
291 break;
292
293 case CF_DIB:
294 case CF_BITMAP:
295 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
296 break;
297
298 default:
299 if (format >= 0xC000)
300 {
301 TCHAR szFormatName[256];
302
303 int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
304
305 if (cActual)
306 {
307 if (strcmp (szFormatName, "HTML Format") == 0)
308 {
309 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
310 }
311 }
312 }
313 break;
314 }
315 }
316
317 CloseClipboard ();
318
319 LogFlow(("vboxClipboardChanged u32Formats %02X\n", u32Formats));
320
321 vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, u32Formats);
322 }
323 else
324 {
325 LogFlow(("vboxClipboardChanged: error in open clipboard. hwnd: %x. err: %Rrc\n", pCtx->hwnd, rc));
326 }
327}
328
329/* Add ourselves into the chain of cliboard listeners */
330static void addToCBChain (VBOXCLIPBOARDCONTEXT *pCtx)
331{
332 if (vboxClipboardIsNewAPI(pCtx))
333 pCtx->pfnAddClipboardFormatListener(pCtx->hwnd);
334 else
335 pCtx->hwndNextInChain = SetClipboardViewer(pCtx->hwnd);
336}
337
338/* Remove ourselves from the chain of cliboard listeners */
339static void removeFromCBChain (VBOXCLIPBOARDCONTEXT *pCtx)
340{
341 if (vboxClipboardIsNewAPI(pCtx))
342 {
343 pCtx->pfnRemoveClipboardFormatListener(pCtx->hwnd);
344 }
345 else
346 {
347 ChangeClipboardChain(pCtx->hwnd, pCtx->hwndNextInChain);
348 pCtx->hwndNextInChain = NULL;
349 }
350}
351
352/* Callback which is invoked when we have successfully pinged ourselves down the
353 * clipboard chain. We simply unset a boolean flag to say that we are responding.
354 * There is a race if a ping returns after the next one is initiated, but nothing
355 * very bad is likely to happen. */
356VOID CALLBACK CBChainPingProc(HWND hwnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult)
357{
358 (void) hwnd;
359 (void) uMsg;
360 (void) lResult;
361 VBOXCLIPBOARDCONTEXT *pCtx = (VBOXCLIPBOARDCONTEXT *)dwData;
362 pCtx->fCBChainPingInProcess = FALSE;
363}
364
365static LRESULT CALLBACK vboxClipboardWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
366{
367 LRESULT rc = 0;
368
369 VBOXCLIPBOARDCONTEXT *pCtx = &g_ctx;
370
371 switch (msg)
372 {
373 case WM_CLIPBOARDUPDATE:
374 {
375 Log(("WM_CLIPBOARDUPDATE\n"));
376
377 if (GetClipboardOwner() != hwnd)
378 {
379 /* Clipboard was updated by another application. */
380 vboxClipboardChanged(pCtx);
381 }
382 } break;
383
384 case WM_CHANGECBCHAIN:
385 {
386 Log(("WM_CHANGECBCHAIN\n"));
387
388 if (vboxClipboardIsNewAPI(pCtx))
389 {
390 rc = DefWindowProc(hwnd, msg, wParam, lParam);
391 break;
392 }
393
394 HWND hwndRemoved = (HWND)wParam;
395 HWND hwndNext = (HWND)lParam;
396
397 if (hwndRemoved == pCtx->hwndNextInChain)
398 {
399 /* The window that was next to our in the chain is being removed.
400 * Relink to the new next window.
401 */
402 pCtx->hwndNextInChain = hwndNext;
403 }
404 else
405 {
406 if (pCtx->hwndNextInChain)
407 {
408 /* Pass the message further. */
409 DWORD_PTR dwResult;
410 rc = SendMessageTimeout(pCtx->hwndNextInChain, WM_CHANGECBCHAIN, wParam, lParam, 0, CBCHAIN_TIMEOUT, &dwResult);
411 if (!rc)
412 rc = (LRESULT)dwResult;
413 }
414 }
415 } break;
416
417 case WM_DRAWCLIPBOARD:
418 {
419 Log(("WM_DRAWCLIPBOARD\n"));
420
421 if (GetClipboardOwner () != hwnd)
422 {
423 /* Clipboard was updated by another application. */
424 vboxClipboardChanged (pCtx);
425 }
426
427 if (pCtx->hwndNextInChain)
428 {
429 Log(("WM_DRAWCLIPBOARD next %p\n", pCtx->hwndNextInChain));
430 /* Pass the message to next windows in the clipboard chain. */
431 DWORD_PTR dwResult;
432 rc = SendMessageTimeout(pCtx->hwndNextInChain, msg, wParam, lParam, 0, CBCHAIN_TIMEOUT, &dwResult);
433 if (!rc)
434 rc = dwResult;
435 }
436 } break;
437
438 case WM_TIMER:
439 {
440 if (vboxClipboardIsNewAPI(pCtx))
441 break;
442
443 HWND hViewer = GetClipboardViewer();
444
445 /* Re-register ourselves in the clipboard chain if our last ping
446 * timed out or there seems to be no valid chain. */
447 if (!hViewer || pCtx->fCBChainPingInProcess)
448 {
449 removeFromCBChain(pCtx);
450 addToCBChain(pCtx);
451 }
452 /* Start a new ping by passing a dummy WM_CHANGECBCHAIN to be
453 * processed by ourselves to the chain. */
454 pCtx->fCBChainPingInProcess = TRUE;
455 hViewer = GetClipboardViewer();
456 if (hViewer)
457 SendMessageCallback(hViewer, WM_CHANGECBCHAIN, (WPARAM)pCtx->hwndNextInChain, (LPARAM)pCtx->hwndNextInChain, CBChainPingProc, (ULONG_PTR) pCtx);
458 } break;
459
460 case WM_RENDERFORMAT:
461 {
462 /* Insert the requested clipboard format data into the clipboard. */
463 uint32_t u32Format = 0;
464
465 UINT format = (UINT)wParam;
466
467 Log(("WM_RENDERFORMAT %d\n", format));
468
469 switch (format)
470 {
471 case CF_UNICODETEXT:
472 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
473 break;
474
475 case CF_DIB:
476 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
477 break;
478
479 default:
480 if (format >= 0xC000)
481 {
482 TCHAR szFormatName[256];
483
484 int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
485
486 if (cActual)
487 {
488 if (strcmp (szFormatName, "HTML Format") == 0)
489 {
490 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
491 }
492 }
493 }
494 break;
495 }
496
497 if (u32Format == 0 || pCtx->pClient == NULL)
498 {
499 /* Unsupported clipboard format is requested. */
500 Log(("WM_RENDERFORMAT unsupported format requested or client is not active.\n"));
501 EmptyClipboard ();
502 }
503 else
504 {
505 int vboxrc = vboxClipboardReadDataFromClient (pCtx, u32Format);
506
507 dprintf(("vboxClipboardReadDataFromClient vboxrc = %d, pv %p, cb %d, u32Format %d\n",
508 vboxrc, pCtx->pClient->data.pv, pCtx->pClient->data.cb, pCtx->pClient->data.u32Format));
509
510 if ( RT_SUCCESS (vboxrc)
511 && pCtx->pClient->data.pv != NULL
512 && pCtx->pClient->data.cb > 0
513 && pCtx->pClient->data.u32Format == u32Format)
514 {
515 HANDLE hMem = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, pCtx->pClient->data.cb);
516
517 dprintf(("hMem %p\n", hMem));
518
519 if (hMem)
520 {
521 void *pMem = GlobalLock (hMem);
522
523 dprintf(("pMem %p, GlobalSize %d\n", pMem, GlobalSize (hMem)));
524
525 if (pMem)
526 {
527 Log(("WM_RENDERFORMAT setting data\n"));
528
529 if (pCtx->pClient->data.pv)
530 {
531 memcpy (pMem, pCtx->pClient->data.pv, pCtx->pClient->data.cb);
532
533 RTMemFree (pCtx->pClient->data.pv);
534 pCtx->pClient->data.pv = NULL;
535 }
536
537 pCtx->pClient->data.cb = 0;
538 pCtx->pClient->data.u32Format = 0;
539
540 /* The memory must be unlocked before inserting to the Clipboard. */
541 GlobalUnlock (hMem);
542
543 /* 'hMem' contains the host clipboard data.
544 * size is 'cb' and format is 'format'.
545 */
546 HANDLE hClip = SetClipboardData (format, hMem);
547
548 dprintf(("vboxClipboardHostEvent hClip %p\n", hClip));
549
550 if (hClip)
551 {
552 /* The hMem ownership has gone to the system. Nothing to do. */
553 break;
554 }
555 }
556
557 GlobalFree (hMem);
558 }
559 }
560
561 RTMemFree (pCtx->pClient->data.pv);
562 pCtx->pClient->data.pv = NULL;
563 pCtx->pClient->data.cb = 0;
564 pCtx->pClient->data.u32Format = 0;
565
566 /* Something went wrong. */
567 EmptyClipboard ();
568 }
569 } break;
570
571 case WM_RENDERALLFORMATS:
572 {
573 Log(("WM_RENDERALLFORMATS\n"));
574
575 /* Do nothing. The clipboard formats will be unavailable now, because the
576 * windows is to be destroyed and therefore the guest side becomes inactive.
577 */
578 int vboxrc = vboxOpenClipboard(hwnd);
579 if (RT_SUCCESS(vboxrc))
580 {
581 EmptyClipboard();
582
583 CloseClipboard();
584 }
585 else
586 {
587 LogFlow(("vboxClipboardWndProc: WM_RENDERALLFORMATS: error in open clipboard. hwnd: %x, rc: %Rrc\n", hwnd, vboxrc));
588 }
589 } break;
590
591 case WM_USER:
592 {
593 if (pCtx->pClient == NULL || pCtx->pClient->fMsgFormats)
594 {
595 /* Host has pending formats message. Ignore the guest announcement,
596 * because host clipboard has more priority.
597 */
598 Log(("WM_USER ignored\n"));
599 break;
600 }
601
602 /* Announce available formats. Do not insert data, they will be inserted in WM_RENDER*. */
603 uint32_t u32Formats = (uint32_t)lParam;
604
605 Log(("WM_USER u32Formats = %02X\n", u32Formats));
606
607 int vboxrc = vboxOpenClipboard(hwnd);
608 if (RT_SUCCESS(vboxrc))
609 {
610 EmptyClipboard();
611
612 Log(("WM_USER emptied clipboard\n"));
613
614 HANDLE hClip = NULL;
615
616 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
617 {
618 dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT\n"));
619
620 hClip = SetClipboardData (CF_UNICODETEXT, NULL);
621 }
622
623 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
624 {
625 dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
626
627 hClip = SetClipboardData (CF_DIB, NULL);
628 }
629
630 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
631 {
632 UINT format = RegisterClipboardFormat ("HTML Format");
633 dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_HTML 0x%04X\n", format));
634 if (format != 0)
635 {
636 hClip = SetClipboardData (format, NULL);
637 }
638 }
639
640 CloseClipboard();
641
642 dprintf(("window proc WM_USER: hClip %p, err %d\n", hClip, GetLastError ()));
643 }
644 else
645 {
646 dprintf(("window proc WM_USER: failed to open clipboard. rc: %Rrc\n", vboxrc));
647 }
648 } break;
649
650 case WM_DESTROY:
651 {
652 /* MS recommends to remove from Clipboard chain in this callback */
653 Assert(pCtx->hwnd);
654 removeFromCBChain(pCtx);
655 if (pCtx->timerRefresh)
656 KillTimer(pCtx->hwnd, 0);
657 PostQuitMessage(0);
658 } break;
659
660 default:
661 {
662 Log(("WM_ %p\n", msg));
663 rc = DefWindowProc(hwnd, msg, wParam, lParam);
664 }
665 }
666
667 Log(("WM_ rc %d\n", rc));
668 return rc;
669}
670
671DECLCALLBACK(int) VBoxClipboardThread (RTTHREAD hThreadSelf, void *pvUser)
672{
673 RT_NOREF2(hThreadSelf, pvUser);
674 /* Create a window and make it a clipboard viewer. */
675 int rc = VINF_SUCCESS;
676
677 LogFlow(("VBoxClipboardThread\n"));
678
679 VBOXCLIPBOARDCONTEXT *pCtx = &g_ctx;
680
681 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
682
683 /* Register the Window Class. */
684 WNDCLASS wc;
685 RT_ZERO(wc);
686
687 wc.style = CS_NOCLOSE;
688 wc.lpfnWndProc = vboxClipboardWndProc;
689 wc.hInstance = hInstance;
690 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
691 wc.lpszClassName = gachWindowClassName;
692
693 ATOM atomWindowClass = RegisterClass(&wc);
694
695 if (atomWindowClass == 0)
696 {
697 Log(("Failed to register window class\n"));
698 rc = VERR_NOT_SUPPORTED;
699 }
700 else
701 {
702 /* Create the window. */
703 pCtx->hwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
704 gachWindowClassName, gachWindowClassName,
705 WS_POPUPWINDOW,
706 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
707
708 if (pCtx->hwnd == NULL)
709 {
710 Log(("Failed to create window\n"));
711 rc = VERR_NOT_SUPPORTED;
712 }
713 else
714 {
715 SetWindowPos(pCtx->hwnd, HWND_TOPMOST, -200, -200, 0, 0,
716 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
717
718 addToCBChain(pCtx);
719 if (!vboxClipboardIsNewAPI(pCtx))
720 pCtx->timerRefresh = SetTimer(pCtx->hwnd, 0, 10 * 1000, NULL);
721
722 MSG msg;
723 BOOL msgret = 0;
724 while ((msgret = GetMessage(&msg, NULL, 0, 0)) > 0)
725 {
726 TranslateMessage(&msg);
727 DispatchMessage(&msg);
728 }
729 /*
730 * Window procedure can return error,
731 * but this is exceptional situation
732 * that should be identified in testing
733 */
734 Assert(msgret >= 0);
735 Log(("VBoxClipboardThread Message loop finished. GetMessage returned %d, message id: %d \n", msgret, msg.message));
736 }
737 }
738
739 pCtx->hwnd = NULL;
740
741 if (atomWindowClass != 0)
742 {
743 UnregisterClass (gachWindowClassName, hInstance);
744 atomWindowClass = 0;
745 }
746
747 return 0;
748}
749
750/*
751 * Public platform dependent functions.
752 */
753int vboxClipboardInit (void)
754{
755 int rc = VINF_SUCCESS;
756
757 RT_ZERO(g_ctx);
758
759 /* Check that new Clipboard API is available */
760 vboxClipboardInitNewAPI(&g_ctx);
761
762 g_ctx.hRenderEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
763
764 rc = RTThreadCreate (&g_ctx.thread, VBoxClipboardThread, NULL, 65536,
765 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
766
767 if (RT_FAILURE (rc))
768 {
769 CloseHandle (g_ctx.hRenderEvent);
770 }
771
772 return rc;
773}
774
775void vboxClipboardDestroy (void)
776{
777 Log(("vboxClipboardDestroy\n"));
778
779 if (g_ctx.hwnd)
780 {
781 PostMessage (g_ctx.hwnd, WM_CLOSE, 0, 0);
782 }
783
784 CloseHandle (g_ctx.hRenderEvent);
785
786 /* Wait for the window thread to terminate. */
787 RTThreadWait (g_ctx.thread, RT_INDEFINITE_WAIT, NULL);
788
789 g_ctx.thread = NIL_RTTHREAD;
790}
791
792int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, bool)
793{
794 Log(("vboxClipboardConnect\n"));
795
796 if (g_ctx.pClient != NULL)
797 {
798 /* One client only. */
799 return VERR_NOT_SUPPORTED;
800 }
801
802 pClient->pCtx = &g_ctx;
803
804 pClient->pCtx->pClient = pClient;
805
806 /* Sync the host clipboard content with the client. */
807 vboxClipboardSync (pClient);
808
809 return VINF_SUCCESS;
810}
811
812int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
813{
814 /* Sync the host clipboard content with the client. */
815 vboxClipboardChanged (pClient->pCtx);
816
817 return VINF_SUCCESS;
818}
819
820void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
821{
822 RT_NOREF1(pClient);
823 Log(("vboxClipboardDisconnect\n"));
824
825 g_ctx.pClient = NULL;
826}
827
828void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
829{
830 /*
831 * The guest announces formats. Forward to the window thread.
832 */
833 PostMessage (pClient->pCtx->hwnd, WM_USER, 0, u32Formats);
834}
835
836int DumpHtml(const char *pszSrc, size_t cb)
837{
838 size_t cchIgnored = 0;
839 int rc = RTStrNLenEx(pszSrc, cb, &cchIgnored);
840 if (RT_SUCCESS(rc))
841 {
842 char *pszBuf = (char *)RTMemAllocZ(cb + 1);
843 if (pszBuf != NULL)
844 {
845 rc = RTStrCopy(pszBuf, cb + 1, (const char *)pszSrc);
846 if (RT_SUCCESS(rc))
847 {
848 for (size_t i = 0; i < cb; ++i)
849 if (pszBuf[i] == '\n' || pszBuf[i] == '\r')
850 pszBuf[i] = ' ';
851 }
852 else
853 Log(("Error in copying string.\n"));
854 Log(("Removed \\r\\n: %s\n", pszBuf));
855 RTMemFree(pszBuf);
856 }
857 else
858 {
859 rc = VERR_NO_MEMORY;
860 Log(("Not enough memory to allocate buffer.\n"));
861 }
862 }
863 return rc;
864}
865
866int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual)
867{
868 LogFlow(("vboxClipboardReadData: u32Format = %02X\n", u32Format));
869
870 HANDLE hClip = NULL;
871
872 /*
873 * The guest wants to read data in the given format.
874 */
875 int rc = vboxOpenClipboard(pClient->pCtx->hwnd);
876 if (RT_SUCCESS(rc))
877 {
878 dprintf(("Clipboard opened.\n"));
879
880 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
881 {
882 hClip = GetClipboardData (CF_DIB);
883
884 if (hClip != NULL)
885 {
886 LPVOID lp = GlobalLock (hClip);
887
888 if (lp != NULL)
889 {
890 dprintf(("CF_DIB\n"));
891
892 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_BITMAP, lp, GlobalSize (hClip),
893 pv, cb, pcbActual);
894
895 GlobalUnlock(hClip);
896 }
897 else
898 {
899 hClip = NULL;
900 }
901 }
902 }
903 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
904 {
905 hClip = GetClipboardData(CF_UNICODETEXT);
906
907 if (hClip != NULL)
908 {
909 LPWSTR uniString = (LPWSTR)GlobalLock (hClip);
910
911 if (uniString != NULL)
912 {
913 dprintf(("CF_UNICODETEXT\n"));
914
915 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, uniString, (lstrlenW (uniString) + 1) * 2,
916 pv, cb, pcbActual);
917
918 GlobalUnlock(hClip);
919 }
920 else
921 {
922 hClip = NULL;
923 }
924 }
925 }
926 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
927 {
928 UINT format = RegisterClipboardFormat ("HTML Format");
929
930 if (format != 0)
931 {
932 hClip = GetClipboardData (format);
933
934 if (hClip != NULL)
935 {
936 LPVOID lp = GlobalLock (hClip);
937
938 if (lp != NULL)
939 {
940 dprintf(("CF_HTML\n"));
941
942 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_HTML, lp, GlobalSize (hClip),
943 pv, cb, pcbActual);
944 LogRelFlowFunc(("Raw HTML clipboard data from host :"));
945 DumpHtml((char*)pv, cb);
946 GlobalUnlock(hClip);
947 }
948 else
949 {
950 hClip = NULL;
951 }
952 }
953 }
954 }
955
956 CloseClipboard ();
957 }
958 else
959 {
960 dprintf(("vboxClipboardReadData: failed to open clipboard, rc: %Rrc\n", rc));
961 }
962
963 if (hClip == NULL)
964 {
965 /* Reply with empty data. */
966 vboxClipboardGetData (0, NULL, 0,
967 pv, cb, pcbActual);
968 }
969
970 return VINF_SUCCESS;
971}
972
973void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)
974{
975 LogFlow(("vboxClipboardWriteData\n"));
976
977 /*
978 * The guest returns data that was requested in the WM_RENDERFORMAT handler.
979 */
980 Assert(pClient->data.pv == NULL && pClient->data.cb == 0 && pClient->data.u32Format == 0);
981
982 vboxClipboardDump(pv, cb, u32Format);
983
984 if (cb > 0)
985 {
986 char *pszResult = NULL;
987
988 if ( u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML
989 && !IsWindowsHTML((const char*)pv))
990 {
991 /* check that this is not already CF_HTML */
992 uint32_t cbResult;
993 int rc = ConvertMimeToCFHTML((const char *)pv, cb, &pszResult, &cbResult);
994 if (RT_SUCCESS(rc))
995 {
996 if (pszResult != NULL && cbResult != 0)
997 {
998 pClient->data.pv = pszResult;
999 pClient->data.cb = cbResult;
1000 pClient->data.u32Format = u32Format;
1001 }
1002 }
1003 }
1004 else
1005 {
1006 pClient->data.pv = RTMemDup(pv, cb);
1007 if (pClient->data.pv)
1008 {
1009 pClient->data.cb = cb;
1010 pClient->data.u32Format = u32Format;
1011 }
1012 }
1013 }
1014
1015 SetEvent(pClient->pCtx->hRenderEvent);
1016}
1017
1018
1019/**
1020 * Extracts field value from CF_HTML struct
1021 *
1022 * @returns VBox status code
1023 * @param pszSrc source in CF_HTML format
1024 * @param pszOption Name of CF_HTML field
1025 * @param puValue Where to return extracted value of CF_HTML field
1026 */
1027static int GetHeaderValue(const char *pszSrc, const char *pszOption, uint32_t *puValue)
1028{
1029 int rc = VERR_INVALID_PARAMETER;
1030
1031 Assert(pszSrc);
1032 Assert(pszOption);
1033
1034 const char *pszOptionValue = RTStrStr(pszSrc, pszOption);
1035 if (pszOptionValue)
1036 {
1037 size_t cchOption = strlen(pszOption);
1038 Assert(cchOption);
1039
1040 rc = RTStrToUInt32Ex(pszOptionValue + cchOption, NULL, 10, puValue);
1041 }
1042 return rc;
1043}
1044
1045
1046/**
1047 * Check that the source string contains CF_HTML struct
1048 *
1049 * @returns @c true if the @source string is in CF_HTML format
1050 */
1051static bool IsWindowsHTML(const char *pszSource)
1052{
1053 return RTStrStr(pszSource, "Version:") != NULL
1054 && RTStrStr(pszSource, "StartHTML:") != NULL;
1055}
1056
1057
1058/*
1059 * Converts clipboard data from CF_HTML format to mimie clipboard format
1060 *
1061 * Returns allocated buffer that contains html converted to text/html mime type
1062 *
1063 * @returns VBox status code.
1064 * @param pszSource The input.
1065 * @param cch The length of the input.
1066 * @param ppszOutput Where to return the result. Free using RTMemFree.
1067 * @param pcbOutput Where to the return length of the result (bytes/chars).
1068 */
1069static int ConvertCFHtmlToMime(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pcbOutput)
1070{
1071 Assert(pszSource);
1072 Assert(cch);
1073 Assert(ppszOutput);
1074 Assert(pcbOutput);
1075
1076 uint32_t offStart;
1077 int rc = GetHeaderValue(pszSource, "StartFragment:", &offStart);
1078 if (RT_SUCCESS(rc))
1079 {
1080 uint32_t offEnd;
1081 rc = GetHeaderValue(pszSource, "EndFragment:", &offEnd);
1082 if (RT_SUCCESS(rc))
1083 {
1084 if ( offStart > 0
1085 && offEnd > 0
1086 && offEnd > offStart
1087 && offEnd <= cch)
1088 {
1089 uint32_t cchSubStr = offEnd - offStart;
1090 char *pszResult = (char *)RTMemAlloc(cchSubStr + 1);
1091 if (pszResult)
1092 {
1093 rc = RTStrCopyEx(pszResult, cchSubStr + 1, pszSource + offStart, cchSubStr);
1094 if (RT_SUCCESS(rc))
1095 {
1096 *ppszOutput = pszResult;
1097 *pcbOutput = (uint32_t)(cchSubStr + 1);
1098 rc = VINF_SUCCESS;
1099 }
1100 else
1101 {
1102 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
1103 RTMemFree(pszResult);
1104 }
1105 }
1106 else
1107 {
1108 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment.\n"));
1109 rc = VERR_NO_MEMORY;
1110 }
1111 }
1112 else
1113 {
1114 LogRelFlowFunc(("Error: CF_HTML out of bounds - offStart=%#x offEnd=%#x cch=%#x\n", offStart, offEnd, cch));
1115 rc = VERR_INVALID_PARAMETER;
1116 }
1117 }
1118 else
1119 {
1120 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc.\n", rc));
1121 rc = VERR_INVALID_PARAMETER;
1122 }
1123 }
1124 else
1125 {
1126 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected StartFragment. rc = %Rrc.\n", rc));
1127 rc = VERR_INVALID_PARAMETER;
1128 }
1129
1130 return rc;
1131}
1132
1133
1134
1135/**
1136 * Converts source UTF-8 MIME HTML clipboard data to UTF-8 CF_HTML format.
1137 *
1138 * This is just encapsulation work, slapping a header on the data.
1139 *
1140 * It allocates
1141 *
1142 * Calculations:
1143 * Header length = format Length + (2*(10 - 5('%010d'))('digits')) - 2('%s') = format length + 8
1144 * EndHtml = Header length + fragment length
1145 * StartHtml = 105(constant)
1146 * StartFragment = 141(constant) may vary if the header html content will be extended
1147 * EndFragment = Header length + fragment length - 38(ending length)
1148 *
1149 * @param pszSource Source buffer that contains utf-16 string in mime html format
1150 * @param cb Size of source buffer in bytes
1151 * @param ppszOutput Where to return the allocated output buffer to put converted UTF-8
1152 * CF_HTML clipboard data. This function allocates memory for this.
1153 * @param pcbOutput Where to return the size of allocated result buffer in bytes/chars, including zero terminator
1154 *
1155 * @note output buffer should be free using RTMemFree()
1156 * @note Everything inside of fragment can be UTF8. Windows allows it. Everything in header should be Latin1.
1157 */
1158static int ConvertMimeToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput)
1159{
1160 Assert(ppszOutput);
1161 Assert(pcbOutput);
1162 Assert(pszSource);
1163 Assert(cb);
1164
1165 /* construct CF_HTML formatted string */
1166 char *pszResult = NULL;
1167 size_t cchFragment;
1168 int rc = RTStrNLenEx(pszSource, cb, &cchFragment);
1169 if (!RT_SUCCESS(rc))
1170 {
1171 LogRelFlowFunc(("Error: invalid source fragment. rc = %Rrc.\n"));
1172 return VERR_INVALID_PARAMETER;
1173 }
1174
1175 /*
1176 @StartHtml - pos before <html>
1177 @EndHtml - whole size of text excluding ending zero char
1178 @StartFragment - pos after <!--StartFragment-->
1179 @EndFragment - pos before <!--EndFragment-->
1180 @note: all values includes CR\LF inserted into text
1181 Calculations:
1182 Header length = format Length + (3*6('digits')) - 2('%s') = format length + 16 (control value - 183)
1183 EndHtml = Header length + fragment length
1184 StartHtml = 105(constant)
1185 StartFragment = 143(constant)
1186 EndFragment = Header length + fragment length - 40(ending length)
1187 */
1188 static const char s_szFormatSample[] =
1189 /* 0: */ "Version:1.0\r\n"
1190 /* 13: */ "StartHTML:000000101\r\n"
1191 /* 34: */ "EndHTML:%0000009u\r\n" // END HTML = Header length + fragment length
1192 /* 53: */ "StartFragment:000000137\r\n"
1193 /* 78: */ "EndFragment:%0000009u\r\n"
1194 /* 101: */ "<html>\r\n"
1195 /* 109: */ "<body>\r\n"
1196 /* 117: */ "<!--StartFragment-->"
1197 /* 137: */ "%s"
1198 /* 137+2: */ "<!--EndFragment-->\r\n"
1199 /* 157+2: */ "</body>\r\n"
1200 /* 166+2: */ "</html>\r\n";
1201 /* 175+2: */
1202 AssertCompile(sizeof(s_szFormatSample) == 175+2+1);
1203
1204 /* calculate parameters of CF_HTML header */
1205 size_t cchHeader = sizeof(s_szFormatSample) - 1;
1206 size_t offEndHtml = cchHeader + cchFragment;
1207 size_t offEndFragment = cchHeader + cchFragment - 38; /* 175-137 = 38 */
1208 pszResult = (char *)RTMemAlloc(offEndHtml + 1);
1209 if (pszResult == NULL)
1210 {
1211 LogRelFlowFunc(("Error: Cannot allocate memory for result buffer. rc = %Rrc.\n"));
1212 return VERR_NO_MEMORY;
1213 }
1214
1215 /* format result CF_HTML string */
1216 size_t cchFormatted = RTStrPrintf(pszResult, offEndHtml + 1,
1217 s_szFormatSample, offEndHtml, offEndFragment, pszSource);
1218 Assert(offEndHtml == cchFormatted); NOREF(cchFormatted);
1219
1220#ifdef VBOX_STRICT
1221 /* Control calculations. check consistency.*/
1222 static const char s_szStartFragment[] = "<!--StartFragment-->";
1223 static const char s_szEndFragment[] = "<!--EndFragment-->";
1224
1225 /* check 'StartFragment:' value */
1226 const char *pszRealStartFragment = RTStrStr(pszResult, s_szStartFragment);
1227 Assert(&pszRealStartFragment[sizeof(s_szStartFragment) - 1] - pszResult == 137);
1228
1229 /* check 'EndFragment:' value */
1230 const char *pszRealEndFragment = RTStrStr(pszResult, s_szEndFragment);
1231 Assert((size_t)(pszRealEndFragment - pszResult) == offEndFragment);
1232#endif
1233
1234 *ppszOutput = pszResult;
1235 *pcbOutput = (uint32_t)cchFormatted + 1;
1236 Assert(*pcbOutput == cchFormatted + 1);
1237
1238 return VINF_SUCCESS;
1239}
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