VirtualBox

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

Last change on this file since 3808 was 3338, checked in by vboxsync, 18 years ago

Export HostServices

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.8 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 as published by the Free Software Foundation,
12 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
13 * distribution. VirtualBox OSE is distributed in the hope that it will
14 * be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * If you received this file as part of a commercial VirtualBox
17 * distribution, then only the terms of your commercial VirtualBox
18 * license agreement apply instead of the previous paragraph.
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/assert.h>
28#include <iprt/thread.h>
29#include <process.h>
30
31#include "VBoxClipboard.h"
32
33#define dprintf Log
34
35static char gachWindowClassName[] = "VBoxSharedClipboardClass";
36
37struct _VBOXCLIPBOARDCONTEXT
38{
39 HWND hwnd;
40 HWND hwndNextInChain;
41
42 RTTHREAD thread;
43 bool fTerminate;
44
45 HANDLE hRenderEvent;
46
47 VBOXCLIPBOARDCLIENTDATA *pClient;
48
49 bool fAnnouncing;
50};
51
52/* Only one client is supported. There seems to be no need for more clients. */
53static VBOXCLIPBOARDCONTEXT g_ctx;
54
55
56#ifdef LOG_ENABLED
57void vboxClipboardDump(const void *pv, size_t cb, uint32_t u32Format)
58{
59 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
60 {
61 Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT:\n"));
62 if (pv && cb)
63 {
64 Log(("%ls\n", pv));
65 }
66 else
67 {
68 Log(("%p %d\n", pv, cb));
69 }
70 }
71 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
72 {
73 dprintf(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
74 }
75 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
76 {
77 Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_HTML:\n"));
78 if (pv && cb)
79 {
80 Log(("%s\n", pv));
81 }
82 else
83 {
84 Log(("%p %d\n", pv, cb));
85 }
86 }
87 else
88 {
89 dprintf(("DUMP: invalid format %02X\n", u32Format));
90 }
91}
92#else
93#define vboxClipboardDump(__pv, __cb, __format) do { NOREF(__pv); NOREF(__cb); NOREF(__format); } while (0)
94#endif /* LOG_ENABLED */
95
96static void vboxClipboardGetData (uint32_t u32Format, const void *pvSrc, uint32_t cbSrc,
97 void *pvDst, uint32_t cbDst, uint32_t *pcbActualDst)
98{
99 dprintf (("vboxClipboardGetData.\n"));
100
101 *pcbActualDst = cbSrc;
102
103 LogFlow(("vboxClipboardGetData cbSrc = %d, cbDst = %d\n", cbSrc, cbDst));
104
105 if (cbSrc > cbDst)
106 {
107 /* Do not copy data. The dst buffer is not enough. */
108 return;
109 }
110
111 memcpy (pvDst, pvSrc, cbSrc);
112
113 vboxClipboardDump(pvDst, cbSrc, u32Format);
114
115 return;
116}
117
118static int vboxClipboardReadDataFromClient (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format)
119{
120 Assert(pCtx->pClient);
121 Assert(pCtx->pClient->data.pv == NULL && pCtx->pClient->data.cb == 0 && pCtx->pClient->data.u32Format == 0);
122
123 LogFlow(("vboxClipboardReadDataFromClient u32Format = %02X\n", u32Format));
124
125 ResetEvent (pCtx->hRenderEvent);
126
127 vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
128
129 WaitForSingleObject(pCtx->hRenderEvent, INFINITE);
130
131 LogFlow(("vboxClipboardReadDataFromClient wait completed\n"));
132
133 return VINF_SUCCESS;
134}
135
136static void vboxClipboardChanged (VBOXCLIPBOARDCONTEXT *pCtx)
137{
138 LogFlow(("vboxClipboardChanged\n"));
139
140 if (pCtx->pClient == NULL)
141 {
142 return;
143 }
144
145 /* Query list of available formats and report to host. */
146 if (OpenClipboard (pCtx->hwnd))
147 {
148 uint32_t u32Formats = 0;
149
150 UINT format = 0;
151
152 while ((format = EnumClipboardFormats (format)) != 0)
153 {
154 LogFlow(("vboxClipboardChanged format %#x\n", format));
155 switch (format)
156 {
157 case CF_UNICODETEXT:
158 case CF_TEXT:
159 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
160 break;
161
162 case CF_DIB:
163 case CF_BITMAP:
164 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
165 break;
166
167 default:
168 if (format >= 0xC000)
169 {
170 TCHAR szFormatName[256];
171
172 int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
173
174 if (cActual)
175 {
176 if (strcmp (szFormatName, "HTML Format") == 0)
177 {
178 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
179 }
180 }
181 }
182 break;
183 }
184 }
185
186 CloseClipboard ();
187
188 LogFlow(("vboxClipboardChanged u32Formats %02X\n", u32Formats));
189
190 vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, u32Formats);
191 }
192}
193
194static LRESULT CALLBACK vboxClipboardWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
195{
196 LRESULT rc = 0;
197
198 VBOXCLIPBOARDCONTEXT *pCtx = &g_ctx;
199
200 switch (msg)
201 {
202 case WM_CHANGECBCHAIN:
203 {
204 Log(("WM_CHANGECBCHAIN\n"));
205
206 HWND hwndRemoved = (HWND)wParam;
207 HWND hwndNext = (HWND)lParam;
208
209 if (hwndRemoved == pCtx->hwndNextInChain)
210 {
211 /* The window that was next to our in the chain is being removed.
212 * Relink to the new next window.
213 */
214 pCtx->hwndNextInChain = hwndNext;
215 }
216 else
217 {
218 if (pCtx->hwndNextInChain)
219 {
220 /* Pass the message further. */
221 rc = SendMessage (pCtx->hwndNextInChain, WM_CHANGECBCHAIN, wParam, lParam);
222 }
223 }
224 } break;
225
226 case WM_DRAWCLIPBOARD:
227 {
228 Log(("WM_DRAWCLIPBOARD next %p\n", pCtx->hwndNextInChain));
229
230 if (!pCtx->fAnnouncing)
231 {
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 pCtx->fAnnouncing = true;
388
389 if (OpenClipboard (hwnd))
390 {
391 EmptyClipboard();
392
393 Log(("WM_USER emptied clipboard\n"));
394
395 HANDLE hClip;
396
397 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
398 {
399 dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT\n"));
400 hClip = SetClipboardData (CF_UNICODETEXT, NULL);
401 }
402
403 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
404 {
405 dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
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\n", hClip));
422 }
423 else
424 {
425 dprintf(("window proc WM_USER: failed to open clipboard\n"));
426 }
427
428 pCtx->fAnnouncing = false;
429
430 } break;
431
432 default:
433 {
434 Log(("WM_ %p\n", msg));
435 rc = DefWindowProc (hwnd, msg, wParam, lParam);
436 }
437 }
438
439 Log(("WM_ rc %d\n", rc));
440 return rc;
441}
442
443DECLCALLBACK(int) VBoxClipboardThread (RTTHREAD ThreadSelf, void *pInstance)
444{
445 /* Create a window and make it a clipboard viewer. */
446 int rc = VINF_SUCCESS;
447
448 LogFlow(("VBoxClipboardThread\n"));
449
450 VBOXCLIPBOARDCONTEXT *pCtx = &g_ctx;
451
452 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle (NULL);
453
454 /* Register the Window Class. */
455 WNDCLASS wc;
456
457 wc.style = CS_NOCLOSE;
458 wc.lpfnWndProc = vboxClipboardWndProc;
459 wc.cbClsExtra = 0;
460 wc.cbWndExtra = 0;
461 wc.hInstance = hInstance;
462 wc.hIcon = NULL;
463 wc.hCursor = NULL;
464 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
465 wc.lpszMenuName = NULL;
466 wc.lpszClassName = gachWindowClassName;
467
468 ATOM atomWindowClass = RegisterClass (&wc);
469
470 if (atomWindowClass == 0)
471 {
472 Log(("Failed to register window class\n"));
473 rc = VERR_NOT_SUPPORTED;
474 }
475 else
476 {
477 /* Create the window. */
478 pCtx->hwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
479 gachWindowClassName, gachWindowClassName,
480 WS_POPUPWINDOW,
481 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
482
483 if (pCtx->hwnd == NULL)
484 {
485 Log(("Failed to create window\n"));
486 rc = VERR_NOT_SUPPORTED;
487 }
488 else
489 {
490 SetWindowPos(pCtx->hwnd, HWND_TOPMOST, -200, -200, 0, 0,
491 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
492
493 pCtx->hwndNextInChain = SetClipboardViewer (pCtx->hwnd);
494
495 MSG msg;
496 while (GetMessage(&msg, NULL, 0, 0) && !pCtx->fTerminate)
497 {
498 TranslateMessage(&msg);
499 DispatchMessage(&msg);
500 }
501 }
502 }
503
504 if (pCtx->hwnd)
505 {
506 ChangeClipboardChain (pCtx->hwnd, pCtx->hwndNextInChain);
507 pCtx->hwndNextInChain = NULL;
508
509 DestroyWindow (pCtx->hwnd);
510 pCtx->hwnd = NULL;
511 }
512
513 if (atomWindowClass != 0)
514 {
515 UnregisterClass (gachWindowClassName, hInstance);
516 atomWindowClass = 0;
517 }
518
519 return 0;
520}
521
522/*
523 * Public platform dependent functions.
524 */
525int vboxClipboardInit (void)
526{
527 int rc = VINF_SUCCESS;
528
529 g_ctx.hRenderEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
530
531 rc = RTThreadCreate (&g_ctx.thread, VBoxClipboardThread, NULL, 65536,
532 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
533
534 return rc;
535}
536
537void vboxClipboardDestroy (void)
538{
539 Log(("vboxClipboardDestroy\n"));
540
541 /* Set the termination flag and ping the window thread. */
542 g_ctx.fTerminate = true;
543
544 if (g_ctx.hwnd)
545 {
546 PostMessage (g_ctx.hwnd, WM_CLOSE, 0, 0);
547 }
548
549 CloseHandle (g_ctx.hRenderEvent);
550
551 /* Wait for the window thread to terminate. */
552 RTThreadWait (g_ctx.thread, RT_INDEFINITE_WAIT, NULL);
553
554 g_ctx.thread = NIL_RTTHREAD;
555}
556
557int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient)
558{
559 Log(("vboxClipboardConnect\n"));
560
561 if (g_ctx.pClient != NULL)
562 {
563 /* One client only. */
564 return VERR_NOT_SUPPORTED;
565 }
566
567 pClient->pCtx = &g_ctx;
568
569 pClient->pCtx->pClient = pClient;
570
571 /* Synch the host clipboard content with the client. */
572 vboxClipboardSync (pClient);
573
574 return VINF_SUCCESS;
575}
576
577int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
578{
579 /* Synch the host clipboard content with the client. */
580 vboxClipboardChanged (pClient->pCtx);
581
582 return VINF_SUCCESS;
583}
584
585void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
586{
587 Log(("vboxClipboardDisconnect\n"));
588
589 g_ctx.pClient = NULL;
590}
591
592void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
593{
594 /*
595 * The guest announces formats. Forward to the window thread.
596 */
597 PostMessage (pClient->pCtx->hwnd, WM_USER, 0, u32Formats);
598}
599
600int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual)
601{
602 LogFlow(("vboxClipboardReadData: u32Format = %02X\n", u32Format));
603
604 HANDLE hClip = NULL;
605
606 /*
607 * The guest wants to read data in the given format.
608 */
609 if (OpenClipboard (pClient->pCtx->hwnd))
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