VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/win32.cpp@ 8083

Last change on this file since 8083 was 7234, checked in by vboxsync, 17 years ago

fTerminate should be volatile.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.4 KB
Line 
1/** @file
2 * Shared Clipboard: Win32 host.
3 */
4
5/*
6 * Copyright (C) 2006-2007 innotek GmbH
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 <process.h>
27
28#include "VBoxClipboard.h"
29
30#define dprintf Log
31
32static char gachWindowClassName[] = "VBoxSharedClipboardClass";
33
34struct _VBOXCLIPBOARDCONTEXT
35{
36 HWND hwnd;
37 HWND hwndNextInChain;
38
39 RTTHREAD thread;
40 bool volatile fTerminate;
41
42 HANDLE hRenderEvent;
43
44 VBOXCLIPBOARDCLIENTDATA *pClient;
45};
46
47/* Only one client is supported. There seems to be no need for more clients. */
48static VBOXCLIPBOARDCONTEXT g_ctx;
49
50
51#ifdef LOG_ENABLED
52void vboxClipboardDump(const void *pv, size_t cb, uint32_t u32Format)
53{
54 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
55 {
56 Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT:\n"));
57 if (pv && cb)
58 {
59 Log(("%ls\n", pv));
60 }
61 else
62 {
63 Log(("%p %d\n", pv, cb));
64 }
65 }
66 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
67 {
68 dprintf(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
69 }
70 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
71 {
72 Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_HTML:\n"));
73 if (pv && cb)
74 {
75 Log(("%s\n", pv));
76 }
77 else
78 {
79 Log(("%p %d\n", pv, cb));
80 }
81 }
82 else
83 {
84 dprintf(("DUMP: invalid format %02X\n", u32Format));
85 }
86}
87#else
88#define vboxClipboardDump(__pv, __cb, __format) do { NOREF(__pv); NOREF(__cb); NOREF(__format); } while (0)
89#endif /* LOG_ENABLED */
90
91static void vboxClipboardGetData (uint32_t u32Format, const void *pvSrc, uint32_t cbSrc,
92 void *pvDst, uint32_t cbDst, uint32_t *pcbActualDst)
93{
94 dprintf (("vboxClipboardGetData.\n"));
95
96 *pcbActualDst = cbSrc;
97
98 LogFlow(("vboxClipboardGetData cbSrc = %d, cbDst = %d\n", cbSrc, cbDst));
99
100 if (cbSrc > cbDst)
101 {
102 /* Do not copy data. The dst buffer is not enough. */
103 return;
104 }
105
106 memcpy (pvDst, pvSrc, cbSrc);
107
108 vboxClipboardDump(pvDst, cbSrc, u32Format);
109
110 return;
111}
112
113static int vboxClipboardReadDataFromClient (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format)
114{
115 Assert(pCtx->pClient);
116 Assert(pCtx->pClient->data.pv == NULL && pCtx->pClient->data.cb == 0 && pCtx->pClient->data.u32Format == 0);
117
118 LogFlow(("vboxClipboardReadDataFromClient u32Format = %02X\n", u32Format));
119
120 ResetEvent (pCtx->hRenderEvent);
121
122 vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
123
124 WaitForSingleObject(pCtx->hRenderEvent, INFINITE);
125
126 LogFlow(("vboxClipboardReadDataFromClient wait completed\n"));
127
128 return VINF_SUCCESS;
129}
130
131static void vboxClipboardChanged (VBOXCLIPBOARDCONTEXT *pCtx)
132{
133 LogFlow(("vboxClipboardChanged\n"));
134
135 if (pCtx->pClient == NULL)
136 {
137 return;
138 }
139
140 /* Query list of available formats and report to host. */
141 if (OpenClipboard (pCtx->hwnd))
142 {
143 uint32_t u32Formats = 0;
144
145 UINT format = 0;
146
147 while ((format = EnumClipboardFormats (format)) != 0)
148 {
149 LogFlow(("vboxClipboardChanged format %#x\n", format));
150 switch (format)
151 {
152 case CF_UNICODETEXT:
153 case CF_TEXT:
154 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
155 break;
156
157 case CF_DIB:
158 case CF_BITMAP:
159 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
160 break;
161
162 default:
163 if (format >= 0xC000)
164 {
165 TCHAR szFormatName[256];
166
167 int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
168
169 if (cActual)
170 {
171 if (strcmp (szFormatName, "HTML Format") == 0)
172 {
173 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
174 }
175 }
176 }
177 break;
178 }
179 }
180
181 CloseClipboard ();
182
183 LogFlow(("vboxClipboardChanged u32Formats %02X\n", u32Formats));
184
185 vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, u32Formats);
186 }
187}
188
189static LRESULT CALLBACK vboxClipboardWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
190{
191 LRESULT rc = 0;
192
193 VBOXCLIPBOARDCONTEXT *pCtx = &g_ctx;
194
195 switch (msg)
196 {
197 case WM_CHANGECBCHAIN:
198 {
199 Log(("WM_CHANGECBCHAIN\n"));
200
201 HWND hwndRemoved = (HWND)wParam;
202 HWND hwndNext = (HWND)lParam;
203
204 if (hwndRemoved == pCtx->hwndNextInChain)
205 {
206 /* The window that was next to our in the chain is being removed.
207 * Relink to the new next window.
208 */
209 pCtx->hwndNextInChain = hwndNext;
210 }
211 else
212 {
213 if (pCtx->hwndNextInChain)
214 {
215 /* Pass the message further. */
216 rc = SendMessage (pCtx->hwndNextInChain, WM_CHANGECBCHAIN, wParam, lParam);
217 }
218 }
219 } break;
220
221 case WM_DRAWCLIPBOARD:
222 {
223 Log(("WM_DRAWCLIPBOARD next %p\n", pCtx->hwndNextInChain));
224
225 if (GetClipboardOwner () != hwnd)
226 {
227 /* Clipboard was updated by another application. */
228 vboxClipboardChanged (pCtx);
229 }
230
231 if (pCtx->hwndNextInChain)
232 {
233 /* Pass the message to next windows in the clipboard chain. */
234 rc = SendMessage (pCtx->hwndNextInChain, msg, wParam, lParam);
235 }
236 } break;
237
238 case WM_CLOSE:
239 {
240 /* Do nothing. Ignore the message. */
241 } break;
242
243 case WM_RENDERFORMAT:
244 {
245 /* Insert the requested clipboard format data into the clipboard. */
246 uint32_t u32Format = 0;
247
248 UINT format = (UINT)wParam;
249
250 Log(("WM_RENDERFORMAT %d\n", format));
251
252 switch (format)
253 {
254 case CF_UNICODETEXT:
255 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
256 break;
257
258 case CF_DIB:
259 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
260 break;
261
262 default:
263 if (format >= 0xC000)
264 {
265 TCHAR szFormatName[256];
266
267 int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
268
269 if (cActual)
270 {
271 if (strcmp (szFormatName, "HTML Format") == 0)
272 {
273 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
274 }
275 }
276 }
277 break;
278 }
279
280 if (u32Format == 0 || pCtx->pClient == NULL)
281 {
282 /* Unsupported clipboard format is requested. */
283 Log(("WM_RENDERFORMAT unsupported format requested or client is not active.\n"));
284 EmptyClipboard ();
285 }
286 else
287 {
288 int vboxrc = vboxClipboardReadDataFromClient (pCtx, u32Format);
289
290 dprintf(("vboxClipboardReadDataFromClient vboxrc = %d\n", vboxrc));
291
292 if ( VBOX_SUCCESS (vboxrc)
293 && pCtx->pClient->data.pv != NULL
294 && pCtx->pClient->data.cb > 0
295 && pCtx->pClient->data.u32Format == u32Format)
296 {
297 HANDLE hMem = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, pCtx->pClient->data.cb);
298
299 dprintf(("hMem %p\n", hMem));
300
301 if (hMem)
302 {
303 void *pMem = GlobalLock (hMem);
304
305 dprintf(("pMem %p, GlobalSize %d\n", pMem, GlobalSize (hMem)));
306
307 if (pMem)
308 {
309 Log(("WM_RENDERFORMAT setting data\n"));
310
311 if (pCtx->pClient->data.pv)
312 {
313 memcpy (pMem, pCtx->pClient->data.pv, pCtx->pClient->data.cb);
314
315 RTMemFree (pCtx->pClient->data.pv);
316 pCtx->pClient->data.pv = NULL;
317 }
318
319 pCtx->pClient->data.cb = 0;
320 pCtx->pClient->data.u32Format = 0;
321
322 /* The memory must be unlocked before inserting to the Clipboard. */
323 GlobalUnlock (hMem);
324
325 /* 'hMem' contains the host clipboard data.
326 * size is 'cb' and format is 'format'.
327 */
328 HANDLE hClip = SetClipboardData (format, hMem);
329
330 dprintf(("vboxClipboardHostEvent hClip %p\n", hClip));
331
332 if (hClip)
333 {
334 /* The hMem ownership has gone to the system. Nothing to do. */
335 break;
336 }
337 }
338
339 GlobalFree (hMem);
340 }
341 }
342
343 RTMemFree (pCtx->pClient->data.pv);
344 pCtx->pClient->data.pv = NULL;
345 pCtx->pClient->data.cb = 0;
346 pCtx->pClient->data.u32Format = 0;
347
348 /* Something went wrong. */
349 EmptyClipboard ();
350 }
351 } break;
352
353 case WM_RENDERALLFORMATS:
354 {
355 Log(("WM_RENDERALLFORMATS\n"));
356
357 /* Do nothing. The clipboard formats will be unavailable now, because the
358 * windows is to be destroyed and therefore the guest side becames inactive.
359 */
360 if (OpenClipboard (hwnd))
361 {
362 EmptyClipboard();
363
364 CloseClipboard();
365 }
366 } break;
367
368 case WM_USER:
369 {
370 if (pCtx->pClient == NULL || pCtx->pClient->fMsgFormats)
371 {
372 /* Host has pending formats message. Ignore the guest announcement,
373 * because host clipboard has more priority.
374 */
375 break;
376 }
377
378 /* Announce available formats. Do not insert data, they will be inserted in WM_RENDER*. */
379 uint32_t u32Formats = (uint32_t)lParam;
380
381 Log(("WM_USER u32Formats = %02X\n", u32Formats));
382
383 if (OpenClipboard (hwnd))
384 {
385 EmptyClipboard();
386
387 Log(("WM_USER emptied clipboard\n"));
388
389 HANDLE hClip = NULL;
390
391 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
392 {
393 dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT\n"));
394
395 hClip = SetClipboardData (CF_UNICODETEXT, NULL);
396 }
397
398 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
399 {
400 dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
401
402 hClip = SetClipboardData (CF_DIB, NULL);
403 }
404
405 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
406 {
407 UINT format = RegisterClipboardFormat ("HTML Format");
408 dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_HTML 0x%04X\n", format));
409 if (format != 0)
410 {
411 hClip = SetClipboardData (format, NULL);
412 }
413 }
414
415 CloseClipboard();
416
417 dprintf(("window proc WM_USER: hClip %p, err %d\n", hClip, GetLastError ()));
418 }
419 else
420 {
421 dprintf(("window proc WM_USER: failed to open clipboard\n"));
422 }
423 } break;
424
425 default:
426 {
427 Log(("WM_ %p\n", msg));
428 rc = DefWindowProc (hwnd, msg, wParam, lParam);
429 }
430 }
431
432 Log(("WM_ rc %d\n", rc));
433 return rc;
434}
435
436DECLCALLBACK(int) VBoxClipboardThread (RTTHREAD ThreadSelf, void *pInstance)
437{
438 /* Create a window and make it a clipboard viewer. */
439 int rc = VINF_SUCCESS;
440
441 LogFlow(("VBoxClipboardThread\n"));
442
443 VBOXCLIPBOARDCONTEXT *pCtx = &g_ctx;
444
445 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle (NULL);
446
447 /* Register the Window Class. */
448 WNDCLASS wc;
449
450 wc.style = CS_NOCLOSE;
451 wc.lpfnWndProc = vboxClipboardWndProc;
452 wc.cbClsExtra = 0;
453 wc.cbWndExtra = 0;
454 wc.hInstance = hInstance;
455 wc.hIcon = NULL;
456 wc.hCursor = NULL;
457 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
458 wc.lpszMenuName = NULL;
459 wc.lpszClassName = gachWindowClassName;
460
461 ATOM atomWindowClass = RegisterClass (&wc);
462
463 if (atomWindowClass == 0)
464 {
465 Log(("Failed to register window class\n"));
466 rc = VERR_NOT_SUPPORTED;
467 }
468 else
469 {
470 /* Create the window. */
471 pCtx->hwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
472 gachWindowClassName, gachWindowClassName,
473 WS_POPUPWINDOW,
474 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
475
476 if (pCtx->hwnd == NULL)
477 {
478 Log(("Failed to create window\n"));
479 rc = VERR_NOT_SUPPORTED;
480 }
481 else
482 {
483 SetWindowPos(pCtx->hwnd, HWND_TOPMOST, -200, -200, 0, 0,
484 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
485
486 pCtx->hwndNextInChain = SetClipboardViewer (pCtx->hwnd);
487
488 MSG msg;
489 while (GetMessage(&msg, NULL, 0, 0) && !pCtx->fTerminate)
490 {
491 TranslateMessage(&msg);
492 DispatchMessage(&msg);
493 }
494 }
495 }
496
497 if (pCtx->hwnd)
498 {
499 ChangeClipboardChain (pCtx->hwnd, pCtx->hwndNextInChain);
500 pCtx->hwndNextInChain = NULL;
501
502 DestroyWindow (pCtx->hwnd);
503 pCtx->hwnd = NULL;
504 }
505
506 if (atomWindowClass != 0)
507 {
508 UnregisterClass (gachWindowClassName, hInstance);
509 atomWindowClass = 0;
510 }
511
512 return 0;
513}
514
515/*
516 * Public platform dependent functions.
517 */
518int vboxClipboardInit (void)
519{
520 int rc = VINF_SUCCESS;
521
522 g_ctx.hRenderEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
523
524 rc = RTThreadCreate (&g_ctx.thread, VBoxClipboardThread, NULL, 65536,
525 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
526
527 if (RT_FAILURE (rc))
528 {
529 CloseHandle (g_ctx.hRenderEvent);
530 }
531
532 return rc;
533}
534
535void vboxClipboardDestroy (void)
536{
537 Log(("vboxClipboardDestroy\n"));
538
539 /* Set the termination flag and ping the window thread. */
540 ASMAtomicWriteBool (&g_ctx.fTerminate, true);
541
542 if (g_ctx.hwnd)
543 {
544 PostMessage (g_ctx.hwnd, WM_CLOSE, 0, 0);
545 }
546
547 CloseHandle (g_ctx.hRenderEvent);
548
549 /* Wait for the window thread to terminate. */
550 RTThreadWait (g_ctx.thread, RT_INDEFINITE_WAIT, NULL);
551
552 g_ctx.thread = NIL_RTTHREAD;
553}
554
555int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient)
556{
557 Log(("vboxClipboardConnect\n"));
558
559 if (g_ctx.pClient != NULL)
560 {
561 /* One client only. */
562 return VERR_NOT_SUPPORTED;
563 }
564
565 pClient->pCtx = &g_ctx;
566
567 pClient->pCtx->pClient = pClient;
568
569 /* Synch the host clipboard content with the client. */
570 vboxClipboardSync (pClient);
571
572 return VINF_SUCCESS;
573}
574
575int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
576{
577 /* Synch the host clipboard content with the client. */
578 vboxClipboardChanged (pClient->pCtx);
579
580 return VINF_SUCCESS;
581}
582
583void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
584{
585 Log(("vboxClipboardDisconnect\n"));
586
587 g_ctx.pClient = NULL;
588}
589
590void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
591{
592 /*
593 * The guest announces formats. Forward to the window thread.
594 */
595 PostMessage (pClient->pCtx->hwnd, WM_USER, 0, u32Formats);
596}
597
598int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual)
599{
600 LogFlow(("vboxClipboardReadData: u32Format = %02X\n", u32Format));
601
602 HANDLE hClip = NULL;
603
604 /*
605 * The guest wants to read data in the given format.
606 */
607 if (OpenClipboard (pClient->pCtx->hwnd))
608 {
609 dprintf(("Clipboard opened.\n"));
610
611 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
612 {
613 hClip = GetClipboardData (CF_DIB);
614
615 if (hClip != NULL)
616 {
617 LPVOID lp = GlobalLock (hClip);
618
619 if (lp != NULL)
620 {
621 dprintf(("CF_DIB\n"));
622
623 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_BITMAP, lp, GlobalSize (hClip),
624 pv, cb, pcbActual);
625
626 GlobalUnlock(hClip);
627 }
628 else
629 {
630 hClip = NULL;
631 }
632 }
633 }
634 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
635 {
636 hClip = GetClipboardData(CF_UNICODETEXT);
637
638 if (hClip != NULL)
639 {
640 LPWSTR uniString = (LPWSTR)GlobalLock (hClip);
641
642 if (uniString != NULL)
643 {
644 dprintf(("CF_UNICODETEXT\n"));
645
646 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, uniString, (lstrlenW (uniString) + 1) * 2,
647 pv, cb, pcbActual);
648
649 GlobalUnlock(hClip);
650 }
651 else
652 {
653 hClip = NULL;
654 }
655 }
656 }
657 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
658 {
659 UINT format = RegisterClipboardFormat ("HTML Format");
660
661 if (format != 0)
662 {
663 hClip = GetClipboardData (format);
664
665 if (hClip != NULL)
666 {
667 LPVOID lp = GlobalLock (hClip);
668
669 if (lp != NULL)
670 {
671 dprintf(("CF_HTML\n"));
672
673 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_HTML, lp, GlobalSize (hClip),
674 pv, cb, pcbActual);
675
676 GlobalUnlock(hClip);
677 }
678 else
679 {
680 hClip = NULL;
681 }
682 }
683 }
684 }
685
686 CloseClipboard ();
687 }
688 else
689 {
690 dprintf(("failed to open clipboard\n"));
691 }
692
693 if (hClip == NULL)
694 {
695 /* Reply with empty data. */
696 vboxClipboardGetData (0, NULL, 0,
697 pv, cb, pcbActual);
698 }
699
700 return VINF_SUCCESS;
701}
702
703void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)
704{
705 LogFlow(("vboxClipboardWriteData\n"));
706
707 /*
708 * The guest returns data that was requested in the WM_RENDERFORMAT handler.
709 */
710 Assert(pClient->data.pv == NULL && pClient->data.cb == 0 && pClient->data.u32Format == 0);
711
712 vboxClipboardDump(pv, cb, u32Format);
713
714 if (cb > 0)
715 {
716 pClient->data.pv = RTMemAlloc (cb);
717
718 if (pClient->data.pv)
719 {
720 memcpy (pClient->data.pv, pv, cb);
721 pClient->data.cb = cb;
722 pClient->data.u32Format = u32Format;
723 }
724 }
725
726 SetEvent(pClient->pCtx->hRenderEvent);
727}
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