VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.6 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
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/assert.h>
24#include <iprt/thread.h>
25#include <process.h>
26
27#include "VBoxClipboard.h"
28
29#define dprintf Log
30
31static char gachWindowClassName[] = "VBoxSharedClipboardClass";
32
33struct _VBOXCLIPBOARDCONTEXT
34{
35 HWND hwnd;
36 HWND hwndNextInChain;
37
38 RTTHREAD thread;
39 bool fTerminate;
40
41 HANDLE hRenderEvent;
42
43 VBOXCLIPBOARDCLIENTDATA *pClient;
44
45 bool fAnnouncing;
46};
47
48/* Only one client is supported. There seems to be no need for more clients. */
49static VBOXCLIPBOARDCONTEXT g_ctx;
50
51
52#ifdef LOG_ENABLED
53void vboxClipboardDump(const void *pv, size_t cb, uint32_t u32Format)
54{
55 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
56 {
57 Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT:\n"));
58 if (pv && cb)
59 {
60 Log(("%ls\n", pv));
61 }
62 else
63 {
64 Log(("%p %d\n", pv, cb));
65 }
66 }
67 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
68 {
69 dprintf(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
70 }
71 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
72 {
73 Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_HTML:\n"));
74 if (pv && cb)
75 {
76 Log(("%s\n", pv));
77 }
78 else
79 {
80 Log(("%p %d\n", pv, cb));
81 }
82 }
83 else
84 {
85 dprintf(("DUMP: invalid format %02X\n", u32Format));
86 }
87}
88#else
89#define vboxClipboardDump(__pv, __cb, __format) do { NOREF(__pv); NOREF(__cb); NOREF(__format); } while (0)
90#endif /* LOG_ENABLED */
91
92static void vboxClipboardGetData (uint32_t u32Format, const void *pvSrc, uint32_t cbSrc,
93 void *pvDst, uint32_t cbDst, uint32_t *pcbActualDst)
94{
95 dprintf (("vboxClipboardGetData.\n"));
96
97 *pcbActualDst = cbSrc;
98
99 LogFlow(("vboxClipboardGetData cbSrc = %d, cbDst = %d\n", cbSrc, cbDst));
100
101 if (cbSrc > cbDst)
102 {
103 /* Do not copy data. The dst buffer is not enough. */
104 return;
105 }
106
107 memcpy (pvDst, pvSrc, cbSrc);
108
109 vboxClipboardDump(pvDst, cbSrc, u32Format);
110
111 return;
112}
113
114static int vboxClipboardReadDataFromClient (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format)
115{
116 Assert(pCtx->pClient);
117 Assert(pCtx->pClient->data.pv == NULL && pCtx->pClient->data.cb == 0 && pCtx->pClient->data.u32Format == 0);
118
119 LogFlow(("vboxClipboardReadDataFromClient u32Format = %02X\n", u32Format));
120
121 ResetEvent (pCtx->hRenderEvent);
122
123 vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
124
125 WaitForSingleObject(pCtx->hRenderEvent, INFINITE);
126
127 LogFlow(("vboxClipboardReadDataFromClient wait completed\n"));
128
129 return VINF_SUCCESS;
130}
131
132static void vboxClipboardChanged (VBOXCLIPBOARDCONTEXT *pCtx)
133{
134 LogFlow(("vboxClipboardChanged\n"));
135
136 if (pCtx->pClient == NULL)
137 {
138 return;
139 }
140
141 /* Query list of available formats and report to host. */
142 if (OpenClipboard (pCtx->hwnd))
143 {
144 uint32_t u32Formats = 0;
145
146 UINT format = 0;
147
148 while ((format = EnumClipboardFormats (format)) != 0)
149 {
150 LogFlow(("vboxClipboardChanged format %#x\n", format));
151 switch (format)
152 {
153 case CF_UNICODETEXT:
154 case CF_TEXT:
155 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
156 break;
157
158 case CF_DIB:
159 case CF_BITMAP:
160 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
161 break;
162
163 default:
164 if (format >= 0xC000)
165 {
166 TCHAR szFormatName[256];
167
168 int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
169
170 if (cActual)
171 {
172 if (strcmp (szFormatName, "HTML Format") == 0)
173 {
174 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
175 }
176 }
177 }
178 break;
179 }
180 }
181
182 CloseClipboard ();
183
184 LogFlow(("vboxClipboardChanged u32Formats %02X\n", u32Formats));
185
186 vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, u32Formats);
187 }
188}
189
190static LRESULT CALLBACK vboxClipboardWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
191{
192 LRESULT rc = 0;
193
194 VBOXCLIPBOARDCONTEXT *pCtx = &g_ctx;
195
196 switch (msg)
197 {
198 case WM_CHANGECBCHAIN:
199 {
200 Log(("WM_CHANGECBCHAIN\n"));
201
202 HWND hwndRemoved = (HWND)wParam;
203 HWND hwndNext = (HWND)lParam;
204
205 if (hwndRemoved == pCtx->hwndNextInChain)
206 {
207 /* The window that was next to our in the chain is being removed.
208 * Relink to the new next window.
209 */
210 pCtx->hwndNextInChain = hwndNext;
211 }
212 else
213 {
214 if (pCtx->hwndNextInChain)
215 {
216 /* Pass the message further. */
217 rc = SendMessage (pCtx->hwndNextInChain, WM_CHANGECBCHAIN, wParam, lParam);
218 }
219 }
220 } break;
221
222 case WM_DRAWCLIPBOARD:
223 {
224 Log(("WM_DRAWCLIPBOARD next %p\n", pCtx->hwndNextInChain));
225
226 if (!pCtx->fAnnouncing)
227 {
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 pCtx->fAnnouncing = true;
384
385 if (OpenClipboard (hwnd))
386 {
387 EmptyClipboard();
388
389 Log(("WM_USER emptied clipboard\n"));
390
391 HANDLE hClip;
392
393 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
394 {
395 dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT\n"));
396 hClip = SetClipboardData (CF_UNICODETEXT, NULL);
397 }
398
399 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
400 {
401 dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
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\n", hClip));
418 }
419 else
420 {
421 dprintf(("window proc WM_USER: failed to open clipboard\n"));
422 }
423
424 pCtx->fAnnouncing = false;
425
426 } break;
427
428 default:
429 {
430 Log(("WM_ %p\n", msg));
431 rc = DefWindowProc (hwnd, msg, wParam, lParam);
432 }
433 }
434
435 Log(("WM_ rc %d\n", rc));
436 return rc;
437}
438
439DECLCALLBACK(int) VBoxClipboardThread (RTTHREAD ThreadSelf, void *pInstance)
440{
441 /* Create a window and make it a clipboard viewer. */
442 int rc = VINF_SUCCESS;
443
444 LogFlow(("VBoxClipboardThread\n"));
445
446 VBOXCLIPBOARDCONTEXT *pCtx = &g_ctx;
447
448 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle (NULL);
449
450 /* Register the Window Class. */
451 WNDCLASS wc;
452
453 wc.style = CS_NOCLOSE;
454 wc.lpfnWndProc = vboxClipboardWndProc;
455 wc.cbClsExtra = 0;
456 wc.cbWndExtra = 0;
457 wc.hInstance = hInstance;
458 wc.hIcon = NULL;
459 wc.hCursor = NULL;
460 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
461 wc.lpszMenuName = NULL;
462 wc.lpszClassName = gachWindowClassName;
463
464 ATOM atomWindowClass = RegisterClass (&wc);
465
466 if (atomWindowClass == 0)
467 {
468 Log(("Failed to register window class\n"));
469 rc = VERR_NOT_SUPPORTED;
470 }
471 else
472 {
473 /* Create the window. */
474 pCtx->hwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
475 gachWindowClassName, gachWindowClassName,
476 WS_POPUPWINDOW,
477 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
478
479 if (pCtx->hwnd == NULL)
480 {
481 Log(("Failed to create window\n"));
482 rc = VERR_NOT_SUPPORTED;
483 }
484 else
485 {
486 SetWindowPos(pCtx->hwnd, HWND_TOPMOST, -200, -200, 0, 0,
487 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
488
489 pCtx->hwndNextInChain = SetClipboardViewer (pCtx->hwnd);
490
491 MSG msg;
492 while (GetMessage(&msg, NULL, 0, 0) && !pCtx->fTerminate)
493 {
494 TranslateMessage(&msg);
495 DispatchMessage(&msg);
496 }
497 }
498 }
499
500 if (pCtx->hwnd)
501 {
502 ChangeClipboardChain (pCtx->hwnd, pCtx->hwndNextInChain);
503 pCtx->hwndNextInChain = NULL;
504
505 DestroyWindow (pCtx->hwnd);
506 pCtx->hwnd = NULL;
507 }
508
509 if (atomWindowClass != 0)
510 {
511 UnregisterClass (gachWindowClassName, hInstance);
512 atomWindowClass = 0;
513 }
514
515 return 0;
516}
517
518/*
519 * Public platform dependent functions.
520 */
521int vboxClipboardInit (void)
522{
523 int rc = VINF_SUCCESS;
524
525 g_ctx.hRenderEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
526
527 rc = RTThreadCreate (&g_ctx.thread, VBoxClipboardThread, NULL, 65536,
528 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
529
530 return rc;
531}
532
533void vboxClipboardDestroy (void)
534{
535 Log(("vboxClipboardDestroy\n"));
536
537 /* Set the termination flag and ping the window thread. */
538 g_ctx.fTerminate = true;
539
540 if (g_ctx.hwnd)
541 {
542 PostMessage (g_ctx.hwnd, WM_CLOSE, 0, 0);
543 }
544
545 CloseHandle (g_ctx.hRenderEvent);
546
547 /* Wait for the window thread to terminate. */
548 RTThreadWait (g_ctx.thread, RT_INDEFINITE_WAIT, NULL);
549
550 g_ctx.thread = NIL_RTTHREAD;
551}
552
553int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient)
554{
555 Log(("vboxClipboardConnect\n"));
556
557 if (g_ctx.pClient != NULL)
558 {
559 /* One client only. */
560 return VERR_NOT_SUPPORTED;
561 }
562
563 pClient->pCtx = &g_ctx;
564
565 pClient->pCtx->pClient = pClient;
566
567 /* Synch the host clipboard content with the client. */
568 vboxClipboardSync (pClient);
569
570 return VINF_SUCCESS;
571}
572
573int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
574{
575 /* Synch the host clipboard content with the client. */
576 vboxClipboardChanged (pClient->pCtx);
577
578 return VINF_SUCCESS;
579}
580
581void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
582{
583 Log(("vboxClipboardDisconnect\n"));
584
585 g_ctx.pClient = NULL;
586}
587
588void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
589{
590 /*
591 * The guest announces formats. Forward to the window thread.
592 */
593 PostMessage (pClient->pCtx->hwnd, WM_USER, 0, u32Formats);
594}
595
596int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual)
597{
598 LogFlow(("vboxClipboardReadData: u32Format = %02X\n", u32Format));
599
600 HANDLE hClip = NULL;
601
602 /*
603 * The guest wants to read data in the given format.
604 */
605 if (OpenClipboard (pClient->pCtx->hwnd))
606 {
607 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
608 {
609 hClip = GetClipboardData (CF_DIB);
610
611 if (hClip != NULL)
612 {
613 LPVOID lp = GlobalLock (hClip);
614
615 if (lp != NULL)
616 {
617 dprintf(("CF_DIB\n"));
618
619 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_BITMAP, lp, GlobalSize (hClip),
620 pv, cb, pcbActual);
621
622 GlobalUnlock(hClip);
623 }
624 else
625 {
626 hClip = NULL;
627 }
628 }
629 }
630 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
631 {
632 hClip = GetClipboardData(CF_UNICODETEXT);
633
634 if (hClip != NULL)
635 {
636 LPWSTR uniString = (LPWSTR)GlobalLock (hClip);
637
638 if (uniString != NULL)
639 {
640 dprintf(("CF_UNICODETEXT\n"));
641
642 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, uniString, (lstrlenW (uniString) + 1) * 2,
643 pv, cb, pcbActual);
644
645 GlobalUnlock(hClip);
646 }
647 else
648 {
649 hClip = NULL;
650 }
651 }
652 }
653 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
654 {
655 UINT format = RegisterClipboardFormat ("HTML Format");
656
657 if (format != 0)
658 {
659 hClip = GetClipboardData (format);
660
661 if (hClip != NULL)
662 {
663 LPVOID lp = GlobalLock (hClip);
664
665 if (lp != NULL)
666 {
667 dprintf(("CF_HTML\n"));
668
669 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_HTML, lp, GlobalSize (hClip),
670 pv, cb, pcbActual);
671
672 GlobalUnlock(hClip);
673 }
674 else
675 {
676 hClip = NULL;
677 }
678 }
679 }
680 }
681
682 CloseClipboard ();
683 }
684 else
685 {
686 dprintf(("failed to open clipboard\n"));
687 }
688
689 if (hClip == NULL)
690 {
691 /* Reply with empty data. */
692 vboxClipboardGetData (0, NULL, 0,
693 pv, cb, pcbActual);
694 }
695
696 return VINF_SUCCESS;
697}
698
699void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)
700{
701 LogFlow(("vboxClipboardWriteData\n"));
702
703 /*
704 * The guest returns data that was requested in the WM_RENDERFORMAT handler.
705 */
706 Assert(pClient->data.pv == NULL && pClient->data.cb == 0 && pClient->data.u32Format == 0);
707
708 vboxClipboardDump(pv, cb, u32Format);
709
710 if (cb > 0)
711 {
712 pClient->data.pv = RTMemAlloc (cb);
713
714 if (pClient->data.pv)
715 {
716 memcpy (pClient->data.pv, pv, cb);
717 pClient->data.cb = cb;
718 pClient->data.u32Format = u32Format;
719 }
720 }
721
722 SetEvent(pClient->pCtx->hRenderEvent);
723}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette