VirtualBox

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

Last change on this file since 8276 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

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