VirtualBox

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

Last change on this file since 62484 was 62467, checked in by vboxsync, 8 years ago

HostServices: scm

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.0 KB
Line 
1/** @file
2 * Shared Clipboard: Win32 host.
3 */
4
5/*
6 * Copyright (C) 2006-2015 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 <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/*Forward declarations*/
45int ConvertMimeToCFHTML(const char *source, size_t cb, char **output, size_t *pcch);
46int ConvertCFHtmlToMime(const char *source, const uint32_t cch, char **output, size_t *pcch);
47bool IsWindowsHTML(const char *source);
48
49
50#ifndef WM_CLIPBOARDUPDATE
51#define WM_CLIPBOARDUPDATE 0x031D
52#endif
53
54struct _VBOXCLIPBOARDCONTEXT
55{
56 HWND hwnd;
57 HWND hwndNextInChain;
58
59 UINT timerRefresh;
60
61 bool fCBChainPingInProcess;
62
63 RTTHREAD thread;
64
65 HANDLE hRenderEvent;
66
67 VBOXCLIPBOARDCLIENTDATA *pClient;
68
69 PFNADDCLIPBOARDFORMATLISTENER pfnAddClipboardFormatListener;
70 PFNREMOVECLIPBOARDFORMATLISTENER pfnRemoveClipboardFormatListener;
71
72};
73
74/* Only one client is supported. There seems to be no need for more clients. */
75static VBOXCLIPBOARDCONTEXT g_ctx;
76
77
78#ifdef LOG_ENABLED
79void vboxClipboardDump(const void *pv, size_t cb, uint32_t u32Format)
80{
81 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
82 {
83 Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT:\n"));
84 if (pv && cb)
85 {
86 Log(("%ls\n", pv));
87 }
88 else
89 {
90 Log(("%p %d\n", pv, cb));
91 }
92 }
93 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
94 {
95 dprintf(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
96 }
97 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
98 {
99 Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_HTML:\n"));
100 if (pv && cb)
101 {
102 Log(("%s\n", pv));
103
104 //size_t cb = RTStrNLen(pv, );
105 char* buf = (char*)RTMemAlloc(cb + 1);
106 RT_BZERO(buf, cb);
107 RTStrCopy(buf, cb, (const char*)pv);
108 for (int i = 0; i < cb; ++i)
109 {
110 if (buf[i] == '\n' || buf[i] == '\r')
111 buf[i] = ' ';
112 }
113
114 Log(("%s\n", buf));
115 RTMemFree(buf);
116 }
117 else
118 {
119 Log(("%p %d\n", pv, cb));
120 }
121 }
122 else
123 {
124 dprintf(("DUMP: invalid format %02X\n", u32Format));
125 }
126}
127#else
128#define vboxClipboardDump(__pv, __cb, __format) do { NOREF(__pv); NOREF(__cb); NOREF(__format); } while (0)
129#endif /* LOG_ENABLED */
130
131
132static void vboxClipboardInitNewAPI(VBOXCLIPBOARDCONTEXT *pCtx)
133{
134 RTLDRMOD hUser32 = NIL_RTLDRMOD;
135 int rc = RTLdrLoadSystem("User32.dll", /* fNoUnload = */ true, &hUser32);
136 if (RT_SUCCESS(rc))
137 {
138 rc = RTLdrGetSymbol(hUser32, "AddClipboardFormatListener", (void**)&pCtx->pfnAddClipboardFormatListener);
139 if (RT_SUCCESS(rc))
140 {
141 rc = RTLdrGetSymbol(hUser32, "RemoveClipboardFormatListener", (void**)&pCtx->pfnRemoveClipboardFormatListener);
142 }
143
144 RTLdrClose(hUser32);
145 }
146
147 if (RT_SUCCESS(rc))
148 {
149 Log(("New Clipboard API is enabled\n"));
150 }
151 else
152 {
153 pCtx->pfnAddClipboardFormatListener = NULL;
154 pCtx->pfnRemoveClipboardFormatListener = NULL;
155 Log(("New Clipboard API is not available. rc = %Rrc\n", rc));
156 }
157}
158
159static bool vboxClipboardIsNewAPI(VBOXCLIPBOARDCONTEXT *pCtx)
160{
161 return pCtx->pfnAddClipboardFormatListener != NULL;
162}
163
164
165static int vboxOpenClipboard(HWND hwnd)
166{
167 /* "OpenClipboard fails if another window has the clipboard open."
168 * So try a few times and wait up to 1 second.
169 */
170 BOOL fOpened = FALSE;
171
172 int i = 0;
173 for (;;)
174 {
175 if (OpenClipboard(hwnd))
176 {
177 fOpened = TRUE;
178 break;
179 }
180
181 if (i >= 10) /* sleep interval = [1..512] ms */
182 break;
183
184 RTThreadSleep(1 << i);
185 ++i;
186 }
187
188#ifdef LOG_ENABLED
189 if (i > 0)
190 LogFlowFunc(("%d times tried to open clipboard.\n", i + 1));
191#endif
192
193 int rc;
194 if (fOpened)
195 rc = VINF_SUCCESS;
196 else
197 {
198 const DWORD err = GetLastError();
199 LogFlowFunc(("error %d\n", err));
200 rc = RTErrConvertFromWin32(err);
201 }
202
203 return rc;
204}
205
206
207static void vboxClipboardGetData (uint32_t u32Format, const void *pvSrc, uint32_t cbSrc,
208 void *pvDst, uint32_t cbDst, uint32_t *pcbActualDst)
209{
210 dprintf (("vboxClipboardGetData.\n"));
211
212 *pcbActualDst = cbSrc;
213
214 LogFlow(("vboxClipboardGetData cbSrc = %d, cbDst = %d\n", cbSrc, cbDst));
215
216 if (cbSrc > cbDst)
217 {
218 /* Do not copy data. The dst buffer is not enough. */
219 return;
220 }
221
222 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML &&
223 IsWindowsHTML((const char*)pvSrc))
224 {
225 char* buffer = NULL;
226 size_t cbuf = 0;
227 ConvertCFHtmlToMime((const char*)pvSrc, cbSrc, (char**)&buffer, &cbuf);
228 if (cbuf > cbDst)
229 {
230 /* Do not copy data. The dst buffer is not enough. */
231 return;
232 }
233 memcpy(pvDst, buffer, cbuf);
234 *pcbActualDst = cbuf;
235 RTMemFree(buffer);
236 }
237 else
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 ThreadSelf, void *pInstance)
672{
673 /* Create a window and make it a clipboard viewer. */
674 int rc = VINF_SUCCESS;
675
676 LogFlow(("VBoxClipboardThread\n"));
677
678 VBOXCLIPBOARDCONTEXT *pCtx = &g_ctx;
679
680 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
681
682 /* Register the Window Class. */
683 WNDCLASS wc;
684 RT_ZERO(wc);
685
686 wc.style = CS_NOCLOSE;
687 wc.lpfnWndProc = vboxClipboardWndProc;
688 wc.hInstance = hInstance;
689 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
690 wc.lpszClassName = gachWindowClassName;
691
692 ATOM atomWindowClass = RegisterClass(&wc);
693
694 if (atomWindowClass == 0)
695 {
696 Log(("Failed to register window class\n"));
697 rc = VERR_NOT_SUPPORTED;
698 }
699 else
700 {
701 /* Create the window. */
702 pCtx->hwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
703 gachWindowClassName, gachWindowClassName,
704 WS_POPUPWINDOW,
705 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
706
707 if (pCtx->hwnd == NULL)
708 {
709 Log(("Failed to create window\n"));
710 rc = VERR_NOT_SUPPORTED;
711 }
712 else
713 {
714 SetWindowPos(pCtx->hwnd, HWND_TOPMOST, -200, -200, 0, 0,
715 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
716
717 addToCBChain(pCtx);
718 if (!vboxClipboardIsNewAPI(pCtx))
719 pCtx->timerRefresh = SetTimer(pCtx->hwnd, 0, 10 * 1000, NULL);
720
721 MSG msg;
722 BOOL msgret = 0;
723 while ((msgret = GetMessage(&msg, NULL, 0, 0)) > 0)
724 {
725 TranslateMessage(&msg);
726 DispatchMessage(&msg);
727 }
728 /*
729 * Window procedure can return error,
730 * but this is exceptional situation
731 * that should be identified in testing
732 */
733 Assert(msgret >= 0);
734 Log(("VBoxClipboardThread Message loop finished. GetMessage returned %d, message id: %d \n", msgret, msg.message));
735 }
736 }
737
738 pCtx->hwnd = NULL;
739
740 if (atomWindowClass != 0)
741 {
742 UnregisterClass (gachWindowClassName, hInstance);
743 atomWindowClass = 0;
744 }
745
746 return 0;
747}
748
749/*
750 * Public platform dependent functions.
751 */
752int vboxClipboardInit (void)
753{
754 int rc = VINF_SUCCESS;
755
756 RT_ZERO(g_ctx);
757
758 /* Check that new Clipboard API is available */
759 vboxClipboardInitNewAPI(&g_ctx);
760
761 g_ctx.hRenderEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
762
763 rc = RTThreadCreate (&g_ctx.thread, VBoxClipboardThread, NULL, 65536,
764 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
765
766 if (RT_FAILURE (rc))
767 {
768 CloseHandle (g_ctx.hRenderEvent);
769 }
770
771 return rc;
772}
773
774void vboxClipboardDestroy (void)
775{
776 Log(("vboxClipboardDestroy\n"));
777
778 if (g_ctx.hwnd)
779 {
780 int rc = PostMessage (g_ctx.hwnd, WM_CLOSE, 0, 0);
781 }
782
783 CloseHandle (g_ctx.hRenderEvent);
784
785 /* Wait for the window thread to terminate. */
786 RTThreadWait (g_ctx.thread, RT_INDEFINITE_WAIT, NULL);
787
788 g_ctx.thread = NIL_RTTHREAD;
789}
790
791int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, bool)
792{
793 Log(("vboxClipboardConnect\n"));
794
795 if (g_ctx.pClient != NULL)
796 {
797 /* One client only. */
798 return VERR_NOT_SUPPORTED;
799 }
800
801 pClient->pCtx = &g_ctx;
802
803 pClient->pCtx->pClient = pClient;
804
805 /* Sync the host clipboard content with the client. */
806 vboxClipboardSync (pClient);
807
808 return VINF_SUCCESS;
809}
810
811int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
812{
813 /* Sync the host clipboard content with the client. */
814 vboxClipboardChanged (pClient->pCtx);
815
816 return VINF_SUCCESS;
817}
818
819void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
820{
821 Log(("vboxClipboardDisconnect\n"));
822
823 g_ctx.pClient = NULL;
824}
825
826void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
827{
828 /*
829 * The guest announces formats. Forward to the window thread.
830 */
831 PostMessage (pClient->pCtx->hwnd, WM_USER, 0, u32Formats);
832}
833
834int DumpHtml(char* src, size_t cb)
835{
836 size_t lenght = 0;
837 int rc = RTStrNLenEx(src, cb, &lenght);
838 if (RT_SUCCESS(rc))
839 {
840 char* buf = (char*)RTMemAlloc(cb + 1);
841 if (buf != NULL)
842 {
843 RT_BZERO(buf, cb + 1);
844 rc = RTStrCopy(buf, cb, (const char*)src);
845 if (RT_SUCCESS(rc))
846 {
847 for (int i = 0; i < cb; ++i)
848 {
849 if (buf[i] == '\n' || buf[i] == '\r')
850 buf[i] = ' ';
851 }
852 }
853 else
854 {
855 Log(("Error in copying string.\n"));
856 }
857 Log(("Removed \\r\\n: %s\n", buf));
858 RTMemFree(buf);
859 }
860 else
861 {
862 rc = VERR_NO_MEMORY;
863 Log(("Not enough memory to allocate buffer.\n"));
864 }
865 }
866 return rc;
867}
868
869int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual)
870{
871 LogFlow(("vboxClipboardReadData: u32Format = %02X\n", u32Format));
872
873 HANDLE hClip = NULL;
874
875 /*
876 * The guest wants to read data in the given format.
877 */
878 int rc = vboxOpenClipboard(pClient->pCtx->hwnd);
879 if (RT_SUCCESS(rc))
880 {
881 dprintf(("Clipboard opened.\n"));
882
883 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
884 {
885 hClip = GetClipboardData (CF_DIB);
886
887 if (hClip != NULL)
888 {
889 LPVOID lp = GlobalLock (hClip);
890
891 if (lp != NULL)
892 {
893 dprintf(("CF_DIB\n"));
894
895 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_BITMAP, lp, GlobalSize (hClip),
896 pv, cb, pcbActual);
897
898 GlobalUnlock(hClip);
899 }
900 else
901 {
902 hClip = NULL;
903 }
904 }
905 }
906 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
907 {
908 hClip = GetClipboardData(CF_UNICODETEXT);
909
910 if (hClip != NULL)
911 {
912 LPWSTR uniString = (LPWSTR)GlobalLock (hClip);
913
914 if (uniString != NULL)
915 {
916 dprintf(("CF_UNICODETEXT\n"));
917
918 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, uniString, (lstrlenW (uniString) + 1) * 2,
919 pv, cb, pcbActual);
920
921 GlobalUnlock(hClip);
922 }
923 else
924 {
925 hClip = NULL;
926 }
927 }
928 }
929 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
930 {
931 UINT format = RegisterClipboardFormat ("HTML Format");
932
933 if (format != 0)
934 {
935 hClip = GetClipboardData (format);
936
937 if (hClip != NULL)
938 {
939 LPVOID lp = GlobalLock (hClip);
940
941 if (lp != NULL)
942 {
943 dprintf(("CF_HTML\n"));
944
945 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_HTML, lp, GlobalSize (hClip),
946 pv, cb, pcbActual);
947 LogRelFlowFunc(("Raw HTML clipboard data from host :"));
948 DumpHtml((char*)pv, cb);
949 GlobalUnlock(hClip);
950 }
951 else
952 {
953 hClip = NULL;
954 }
955 }
956 }
957 }
958
959 CloseClipboard ();
960 }
961 else
962 {
963 dprintf(("vboxClipboardReadData: failed to open clipboard, rc: %Rrc\n", rc));
964 }
965
966 if (hClip == NULL)
967 {
968 /* Reply with empty data. */
969 vboxClipboardGetData (0, NULL, 0,
970 pv, cb, pcbActual);
971 }
972
973 return VINF_SUCCESS;
974}
975
976void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)
977{
978 LogFlow(("vboxClipboardWriteData\n"));
979
980 /*
981 * The guest returns data that was requested in the WM_RENDERFORMAT handler.
982 */
983 Assert(pClient->data.pv == NULL && pClient->data.cb == 0 && pClient->data.u32Format == 0);
984
985 vboxClipboardDump(pv, cb, u32Format);
986
987 if (cb > 0)
988 {
989 char* pszResult = NULL;
990 size_t cch;
991
992 if(u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML &&
993 !IsWindowsHTML((const char*)pv))
994 {
995 /* check that this is not already CF_HTML */
996 int rc = ConvertMimeToCFHTML((const char*)pv, cb, &pszResult, &cch);
997 if (RT_SUCCESS(rc))
998 {
999 if (pszResult != NULL && cch != 0)
1000 {
1001 pClient->data.pv = pszResult;
1002 pClient->data.cb = cch;
1003 pClient->data.u32Format = u32Format;
1004 }
1005 }
1006 }
1007 else
1008 {
1009 pClient->data.pv = RTMemAlloc (cb);
1010 if (pClient->data.pv)
1011 {
1012 memcpy (pClient->data.pv, pv, cb);
1013 pClient->data.cb = cb;
1014 pClient->data.u32Format = u32Format;
1015 }
1016 }
1017 }
1018
1019 SetEvent(pClient->pCtx->hRenderEvent);
1020}
1021
1022/*
1023@StartHtml - pos before <html>
1024@EndHtml - whole size of text excluding ending zero char
1025@StartFragment - pos after <!--StartFragment-->
1026@EndFragment - pos before <!--EndFragment-->
1027@note: all values includes CR\LF inserted into text
1028Calculations:
1029Header length = format Length + (3*6('digits')) - 2('%s') = format length + 16 (control value - 183)
1030EndHtml = Header length + fragment length
1031StartHtml = 105(constant)
1032StartFragment = 143(constant)
1033EndFragment = Header length + fragment length - 40(ending length)
1034*/
1035const char pcszFormatSample[] =
1036 "Version:1.0\r\n"
1037 "StartHTML:000000101\r\n"
1038 "EndHTML:%09d\r\n" // END HTML = Header length + fragment lengh
1039"StartFragment:000000137\r\n"
1040"EndFragment:%09d\r\n"
1041"<html>\r\n"
1042"<body>\r\n"
1043"<!--StartFragment-->%s<!--EndFragment-->\r\n"
1044"</body>\r\n"
1045"</html>\r\n";
1046
1047/*
1048* Extracts field value from CF_HTML struct
1049* @src - source in CF_HTML format
1050* @option - name of CF_HTML field
1051* @value - extracted value of CF_HTML field
1052* returns RC result code
1053*/
1054int GetHeaderValue(const char *pcszSrc, const char *pcszOption, size_t *pcValue)
1055{
1056 size_t cOptionLenght = 0;
1057 int rc = VERR_INVALID_PARAMETER;
1058
1059 Assert(pcszSrc);
1060 Assert(pcszOption);
1061
1062 char* pcszOptionValue = RTStrStr(pcszSrc, pcszOption);
1063 if (pcszOptionValue)
1064 {
1065 rc = RTStrNLenEx(pcszOption, RTSTR_MAX, &cOptionLenght);
1066 Assert(cOptionLenght);
1067 if (RT_SUCCESS(rc))
1068 {
1069 int32_t tmpValue;
1070 rc = RTStrToInt32Ex(pcszOptionValue + cOptionLenght, NULL, 10, &tmpValue);
1071 if (RT_SUCCESS(rc))
1072 {
1073 *pcValue = tmpValue;
1074 rc = VINF_SUCCESS;
1075 }
1076 }
1077 }
1078 return rc;
1079}
1080
1081/*
1082 * Check that the source string contains CF_HTML struct
1083 * returns true if the @source string is in CF_HTML format
1084 */
1085bool IsWindowsHTML(const char *pcszSource)
1086{
1087 return RTStrStr(pcszSource, "Version:") != NULL
1088 && RTStrStr(pcszSource, "StartHTML:") != NULL;
1089}
1090
1091
1092/*
1093* Converts clipboard data from CF_HTML format to mimie clipboard format
1094* Returns allocated buffer that contains html converted to text/html mime type
1095* return result code
1096* parameters - output buffer and size of output buffer
1097* It allocates the buffer needed for storing converted fragment
1098* Allocated buffer should be destroyed by RTMemFree after usage
1099*/
1100int ConvertCFHtmlToMime(const char *pcszSource, const uint32_t cch, char **ppszOutput, size_t *pcCh)
1101{
1102 char* result = NULL;
1103
1104 Assert(pcszSource);
1105 Assert(cch);
1106 Assert(ppszOutput);
1107 Assert(pcCh);
1108
1109 size_t cStartOffset, cEndOffset;
1110 int rc = GetHeaderValue(pcszSource, "StartFragment:", &cStartOffset);
1111 if (!RT_SUCCESS(rc))
1112 {
1113 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected StartFragment. rc = %Rrc.\n", rc));
1114 return VERR_INVALID_PARAMETER;
1115 }
1116 rc = GetHeaderValue(pcszSource, "EndFragment:", &cEndOffset);
1117 if (!RT_SUCCESS(rc))
1118 {
1119 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc.\n", rc));
1120 return VERR_INVALID_PARAMETER;
1121 }
1122 if (cStartOffset > 0 && cEndOffset > 0 && cEndOffset > cStartOffset)
1123 {
1124 size_t cSubstrlen = cEndOffset - cStartOffset;
1125 result = (char*)RTMemAlloc(cSubstrlen + 1);
1126 if (result)
1127 {
1128 RT_BZERO(result, cSubstrlen + 1);
1129 rc = RTStrCopyEx(result, cSubstrlen + 1, pcszSource + cStartOffset, cSubstrlen);
1130 if (RT_SUCCESS(rc))
1131 {
1132 *ppszOutput = result;
1133 *pcCh = cSubstrlen + 1;
1134 }
1135 else
1136 {
1137 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
1138 return rc;
1139 }
1140 }
1141 else
1142 {
1143 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment.\n"));
1144 return VERR_NO_MEMORY;
1145 }
1146 }
1147
1148return VINF_SUCCESS;
1149}
1150
1151
1152
1153/*
1154* Converts source Utf16 mime html clipboard data to Utf8 CF_HTML format
1155* It allocates
1156* Calculations:
1157* Header length = format Length + (2*(10 - 5('%010d'))('digits')) - 2('%s') = format length + 8
1158* EndHtml = Header length + fragment length
1159* StartHtml = 105(constant)
1160* StartFragment = 141(constant) may vary if the header html content will be extended
1161* EndFragment = Header length + fragment length - 38(ending length)
1162* @source: source buffer that contains utf-16 string in mime html format
1163* @cb: size of source buffer in bytes
1164* @output: allocated output buffer to put converted Utf8 CF_HTML clipboard data. This function allocates memory for this.
1165* @pcch: size of allocated result buffer in bytes
1166* @note: output buffer should be free using RTMemFree()
1167* @note: Everything inside of fragment can be UTF8. Windows allows it. Everything in header should be Latin1.
1168*/
1169int ConvertMimeToCFHTML(const char *pcszSource, size_t cb, char **pcszOutput, size_t *pcCh)
1170{
1171 Assert(pcszOutput);
1172 Assert(pcCh);
1173 Assert(pcszSource);
1174 Assert(cb);
1175
1176 size_t cFragmentLength = 0;
1177
1178 char* pszBuf = (char*)pcszSource;
1179
1180 /* construct CF_HTML formatted string */
1181 char* pszResult = NULL;
1182 int rc = RTStrNLenEx(pszBuf, RTSTR_MAX, &cFragmentLength);
1183 if (!RT_SUCCESS(rc))
1184 {
1185 LogRelFlowFunc(("Error: invalid source fragment. rc = %Rrc.\n"));
1186 return VERR_INVALID_PARAMETER;
1187 }
1188
1189 /* caluclate parameters of CF_HTML header */
1190 size_t cHeaderLength = (sizeof(pcszFormatSample) - 1) + 8;
1191 size_t cEndHtml = cHeaderLength + cFragmentLength;
1192 size_t cEndFragment = cHeaderLength + cFragmentLength - 38;
1193 pszResult = (char*)RTMemAlloc(cEndHtml + 1);
1194 if (pszResult == NULL)
1195 {
1196 LogRelFlowFunc(("Error: Cannot allocate memory for result buffer. rc = %Rrc.\n"));
1197 return VERR_NO_MEMORY;
1198 }
1199
1200 /* format result CF_HTML string */
1201 rc = RTStrPrintf(pszResult, cEndHtml + 1, pcszFormatSample, cEndHtml, cEndFragment, pszBuf);
1202 if (rc == -1)
1203 {
1204 LogRelFlowFunc(("Error: cannot construct CF_HTML. rc = %Rrc.\n"));
1205 return VERR_CANT_CREATE;
1206 }
1207 Assert(cEndHtml == rc);
1208
1209#ifdef DEBUG
1210 {
1211 /*Control calculations. check consistency.*/
1212 const char pcszStartFragment[] = "<!--StartFragment-->";
1213 const char pcszEndFragment[] = "<!--EndFragment-->";
1214
1215 /* check 'StartFragment:' value */
1216 const char* pcszRealStartFragment = RTStrStr(pszResult, pcszStartFragment);
1217 Assert((pcszRealStartFragment + sizeof(pcszStartFragment) - 1) - pszResult == 137);//141);
1218
1219 /* check 'EndFragment:' value */
1220 const char* pcszRealEndFragment = RTStrStr(pszResult, pcszEndFragment);
1221 Assert((pcszRealEndFragment - pszResult) == cEndFragment);
1222 }
1223#endif
1224
1225 *pcszOutput = pszResult;
1226 *pcCh = rc+1;
1227
1228 return VINF_SUCCESS;
1229}
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