VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxClipboard.cpp@ 29813

Last change on this file since 29813 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.5 KB
Line 
1/** @file
2 *
3 * VBoxClipboard - Shared clipboard
4 *
5 */
6
7/*
8 * Copyright (C) 2006-2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include "VBoxTray.h"
20#include "helpers.h"
21
22#include <VBox/HostServices/VBoxClipboardSvc.h>
23#include <strsafe.h>
24
25typedef struct _VBOXCLIPBOARDCONTEXT
26{
27 const VBOXSERVICEENV *pEnv;
28
29 uint32_t u32ClientID;
30
31 ATOM atomWindowClass;
32
33 HWND hwnd;
34
35 HWND hwndNextInChain;
36
37// bool fOperational;
38
39// uint32_t u32LastSentFormat;
40// uint64_t u64LastSentCRC64;
41
42} VBOXCLIPBOARDCONTEXT;
43
44static char gachWindowClassName[] = "VBoxSharedClipboardClass";
45
46
47static int vboxClipboardChanged(VBOXCLIPBOARDCONTEXT *pCtx)
48{
49 AssertPtr(pCtx);
50
51 /* Query list of available formats and report to host. */
52 int rc = VINF_SUCCESS;
53 if (FALSE == OpenClipboard(pCtx->hwnd))
54 {
55 rc = RTErrConvertFromWin32(GetLastError());
56 }
57 else
58 {
59 uint32_t u32Formats = 0;
60 UINT format = 0;
61
62 while ((format = EnumClipboardFormats (format)) != 0)
63 {
64 Log(("vboxClipboardChanged: format = 0x%08X\n", format));
65 switch (format)
66 {
67 case CF_UNICODETEXT:
68 case CF_TEXT:
69 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
70 break;
71
72 case CF_DIB:
73 case CF_BITMAP:
74 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
75 break;
76
77 default:
78 if (format >= 0xC000)
79 {
80 TCHAR szFormatName[256];
81
82 int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
83 if (cActual)
84 {
85 if (strcmp (szFormatName, "HTML Format") == 0)
86 {
87 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
88 }
89 }
90 }
91 break;
92 }
93 }
94
95 CloseClipboard ();
96 rc = VbglR3ClipboardReportFormats(pCtx->u32ClientID, u32Formats);
97 }
98 return rc;
99}
100
101static LRESULT vboxClipboardProcessMsg(VBOXCLIPBOARDCONTEXT *pCtx, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
102{
103 LRESULT rc = 0;
104
105 switch (msg)
106 {
107 case WM_CHANGECBCHAIN:
108 {
109 HWND hwndRemoved = (HWND)wParam;
110 HWND hwndNext = (HWND)lParam;
111
112 Log(("vboxClipboardProcessMsg: WM_CHANGECBCHAIN: hwndRemoved %p, hwndNext %p, hwnd %p\n", hwndRemoved, hwndNext, pCtx->hwnd));
113
114 if (hwndRemoved == pCtx->hwndNextInChain)
115 {
116 /* The window that was next to our in the chain is being removed.
117 * Relink to the new next window. */
118 pCtx->hwndNextInChain = hwndNext;
119 }
120 else
121 {
122 if (pCtx->hwndNextInChain)
123 {
124 /* Pass the message further. */
125 rc = SendMessage(pCtx->hwndNextInChain, WM_CHANGECBCHAIN, wParam, lParam);
126 }
127 }
128 } break;
129
130 case WM_DRAWCLIPBOARD:
131 {
132 Log(("vboxClipboardProcessMsg: WM_DRAWCLIPBOARD, hwnd %p\n", pCtx->hwnd));
133
134 if (GetClipboardOwner () != hwnd)
135 {
136 /* Clipboard was updated by another application. */
137 /* WM_DRAWCLIPBOARD always expects a return code of 0, so don't change "rc" here. */
138 int vboxrc = vboxClipboardChanged(pCtx);
139 if (RT_FAILURE(vboxrc))
140 Log(("vboxClipboardProcessMsg: vboxClipboardChanged failed, rc = %Rrc\n", vboxrc));
141 }
142
143 /* Pass the message to next windows in the clipboard chain. */
144 SendMessage(pCtx->hwndNextInChain, msg, wParam, lParam);
145 } break;
146
147 case WM_CLOSE:
148 {
149 /* Do nothing. Ignore the message. */
150 } break;
151
152 case WM_RENDERFORMAT:
153 {
154 /* Insert the requested clipboard format data into the clipboard. */
155 uint32_t u32Format = 0;
156 UINT format = (UINT)wParam;
157
158 Log(("vboxClipboardProcessMsg: WM_RENDERFORMAT, format = %x\n", format));
159 switch (format)
160 {
161 case CF_UNICODETEXT:
162 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
163 break;
164
165 case CF_DIB:
166 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
167 break;
168
169 default:
170 if (format >= 0xC000)
171 {
172 TCHAR szFormatName[256];
173
174 int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
175 if (cActual)
176 {
177 if (strcmp (szFormatName, "HTML Format") == 0)
178 {
179 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
180 }
181 }
182 }
183 break;
184 }
185
186 if (u32Format == 0)
187 {
188 /* Unsupported clipboard format is requested. */
189 Log(("vboxClipboardProcessMsg: Unsupported clipboard format requested: %ld\n", u32Format));
190 EmptyClipboard();
191 }
192 else
193 {
194 const uint32_t cbPrealloc = 4096; /* @todo r=andy Make it dynamic for supporting larger text buffers! */
195 uint32_t cb = 0;
196
197 /* Preallocate a buffer, most of small text transfers will fit into it. */
198 HANDLE hMem = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, cbPrealloc);
199 Log(("vboxClipboardProcessMsg: Preallocated handle hMem = %p\n", hMem));
200
201 if (hMem)
202 {
203 void *pMem = GlobalLock(hMem);
204 Log(("vboxClipboardProcessMsg: Locked pMem = %p, GlobalSize = %ld\n", pMem, GlobalSize(hMem)));
205
206 if (pMem)
207 {
208 /* Read the host data to the preallocated buffer. */
209 int vboxrc = VbglR3ClipboardReadData(pCtx->u32ClientID, u32Format, pMem, cbPrealloc, &cb);
210 Log(("vboxClipboardProcessMsg: VbglR3ClipboardReadData returned with rc = %Rrc\n", vboxrc));
211
212 if (RT_SUCCESS(vboxrc))
213 {
214 if (cb == 0)
215 {
216 /* 0 bytes returned means the clipboard is empty.
217 * Deallocate the memory and set hMem to NULL to get to
218 * the clipboard empty code path. */
219 GlobalUnlock(hMem);
220 GlobalFree(hMem);
221 hMem = NULL;
222 }
223 else if (cb > cbPrealloc)
224 {
225 GlobalUnlock(hMem);
226
227 /* The preallocated buffer is too small, adjust the size. */
228 hMem = GlobalReAlloc(hMem, cb, 0);
229 Log(("vboxClipboardProcessMsg: Reallocated hMem = %p\n", hMem));
230
231 if (hMem)
232 {
233 pMem = GlobalLock(hMem);
234 Log(("vboxClipboardProcessMsg: Locked pMem = %p, GlobalSize = %ld\n", pMem, GlobalSize(hMem)));
235
236 if (pMem)
237 {
238 /* Read the host data to the preallocated buffer. */
239 uint32_t cbNew = 0;
240 vboxrc = VbglR3ClipboardReadData(pCtx->u32ClientID, u32Format, pMem, cb, &cbNew);
241 Log(("VbglR3ClipboardReadData returned with rc = %Rrc, cb = %d, cbNew = %d\n", vboxrc, cb, cbNew));
242
243 if (RT_SUCCESS (vboxrc) && cbNew <= cb)
244 {
245 cb = cbNew;
246 }
247 else
248 {
249 GlobalUnlock(hMem);
250 GlobalFree(hMem);
251 hMem = NULL;
252 }
253 }
254 else
255 {
256 GlobalFree(hMem);
257 hMem = NULL;
258 }
259 }
260 }
261
262 if (hMem)
263 {
264 /* pMem is the address of the data. cb is the size of returned data. */
265 /* Verify the size of returned text, the memory block for clipboard
266 * must have the exact string size.
267 */
268 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
269 {
270 size_t cbActual = 0;
271 HRESULT hrc = StringCbLengthW((LPWSTR)pMem, cb, &cbActual);
272 if (FAILED (hrc))
273 {
274 /* Discard invalid data. */
275 GlobalUnlock(hMem);
276 GlobalFree(hMem);
277 hMem = NULL;
278 }
279 else
280 {
281 /* cbActual is the number of bytes, excluding those used
282 * for the terminating null character.
283 */
284 cb = (uint32_t)(cbActual + 2);
285 }
286 }
287 }
288
289 if (hMem)
290 {
291 GlobalUnlock(hMem);
292
293 hMem = GlobalReAlloc(hMem, cb, 0);
294 Log(("vboxClipboardProcessMsg: Reallocated hMem = %p\n", hMem));
295
296 if (hMem)
297 {
298 /* 'hMem' contains the host clipboard data.
299 * size is 'cb' and format is 'format'. */
300 HANDLE hClip = SetClipboardData(format, hMem);
301 Log(("vboxClipboardProcessMsg: WM_RENDERFORMAT hClip = %p\n", hClip));
302
303 if (hClip)
304 {
305 /* The hMem ownership has gone to the system. Finish the processing. */
306 break;
307 }
308
309 /* Cleanup follows. */
310 }
311 }
312 }
313 if (hMem)
314 GlobalUnlock(hMem);
315 }
316 if (hMem)
317 GlobalFree(hMem);
318 }
319
320 /* Something went wrong. */
321 EmptyClipboard();
322 }
323 } break;
324
325 case WM_RENDERALLFORMATS:
326 {
327 /* Do nothing. The clipboard formats will be unavailable now, because the
328 * windows is to be destroyed and therefore the guest side becomes inactive.
329 */
330 if (OpenClipboard(hwnd))
331 {
332 EmptyClipboard();
333 CloseClipboard();
334 }
335 } break;
336
337 case WM_USER:
338 {
339 /* Announce available formats. Do not insert data, they will be inserted in WM_RENDER*. */
340 uint32_t u32Formats = (uint32_t)lParam;
341
342 if (FALSE == OpenClipboard(hwnd))
343 {
344 Log(("vboxClipboardProcessMsg: WM_USER: Failed to open clipboard! Last error = %ld\n", GetLastError()));
345 }
346 else
347 {
348 EmptyClipboard();
349
350 HANDLE hClip = NULL;
351
352 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
353 {
354 Log(("vboxClipboardProcessMsg: WM_USER: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT\n"));
355 hClip = SetClipboardData(CF_UNICODETEXT, NULL);
356 }
357
358 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
359 {
360 Log(("vboxClipboardProcessMsg: WM_USER: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
361 hClip = SetClipboardData(CF_DIB, NULL);
362 }
363
364 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
365 {
366 UINT format = RegisterClipboardFormat ("HTML Format");
367 Log(("vboxClipboardProcessMsg: WM_USER: VBOX_SHARED_CLIPBOARD_FMT_HTML 0x%04X\n", format));
368 if (format != 0)
369 {
370 hClip = SetClipboardData(format, NULL);
371 }
372 }
373
374 CloseClipboard();
375 Log(("vboxClipboardProcessMsg: WM_USER: hClip = %p, err = %ld\n", hClip, GetLastError ()));
376 }
377 } break;
378
379 case WM_USER + 1:
380 {
381 /* Send data in the specified format to the host. */
382 uint32_t u32Formats = (uint32_t)lParam;
383 HANDLE hClip = NULL;
384
385 if (FALSE == OpenClipboard(hwnd))
386 {
387 Log(("vboxClipboardProcessMsg: WM_USER: Failed to open clipboard! Last error = %ld\n", GetLastError()));
388 }
389 else
390 {
391 int vboxrc;
392 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
393 {
394 hClip = GetClipboardData(CF_DIB);
395
396 if (hClip != NULL)
397 {
398 LPVOID lp = GlobalLock(hClip);
399 if (lp != NULL)
400 {
401 Log(("vboxClipboardProcessMsg: WM_USER + 1: CF_DIB\n"));
402 vboxrc = VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_BITMAP,
403 lp, GlobalSize(hClip));
404 GlobalUnlock(hClip);
405 }
406 else
407 {
408 hClip = NULL;
409 }
410 }
411 }
412 else if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
413 {
414 hClip = GetClipboardData(CF_UNICODETEXT);
415
416 if (hClip != NULL)
417 {
418 LPWSTR uniString = (LPWSTR)GlobalLock(hClip);
419
420 if (uniString != NULL)
421 {
422 Log(("vboxClipboardProcessMsg: WM_USER + 1: CF_UNICODETEXT\n"));
423 vboxrc = VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
424 uniString, (lstrlenW(uniString) + 1) * 2);
425 GlobalUnlock(hClip);
426 }
427 else
428 {
429 hClip = NULL;
430 }
431 }
432 }
433 else if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
434 {
435 UINT format = RegisterClipboardFormat ("HTML Format");
436 if (format != 0)
437 {
438 hClip = GetClipboardData(format);
439 if (hClip != NULL)
440 {
441 LPVOID lp = GlobalLock(hClip);
442
443 if (lp != NULL)
444 {
445 Log(("vboxClipboardProcessMsg: WM_USER + 1: CF_HTML\n"));
446 vboxrc = VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_HTML,
447 lp, GlobalSize(hClip));
448 GlobalUnlock(hClip);
449 }
450 else
451 {
452 hClip = NULL;
453 }
454 }
455 }
456 }
457
458 CloseClipboard();
459 }
460
461 if (hClip == NULL)
462 {
463 /* Requested clipboard format is not available, send empty data. */
464 VbglR3ClipboardWriteData(pCtx->u32ClientID, 0, NULL, 0);
465 }
466 } break;
467
468 default:
469 {
470 rc = DefWindowProc(hwnd, msg, wParam, lParam);
471 }
472 }
473
474 Log(("vboxClipboardProcessMsg returned with rc = %ld\n", rc));
475 return rc;
476}
477
478static LRESULT CALLBACK vboxClipboardWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
479
480static int vboxClipboardInit (VBOXCLIPBOARDCONTEXT *pCtx)
481{
482 /* Register the Window Class. */
483 WNDCLASS wc;
484
485 wc.style = CS_NOCLOSE;
486 wc.lpfnWndProc = vboxClipboardWndProc;
487 wc.cbClsExtra = 0;
488 wc.cbWndExtra = 0;
489 wc.hInstance = pCtx->pEnv->hInstance;
490 wc.hIcon = NULL;
491 wc.hCursor = NULL;
492 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
493 wc.lpszMenuName = NULL;
494 wc.lpszClassName = gachWindowClassName;
495
496 pCtx->atomWindowClass = RegisterClass (&wc);
497
498 int rc = VINF_SUCCESS;
499 if (pCtx->atomWindowClass == 0)
500 {
501 rc = VERR_NOT_SUPPORTED;
502 }
503 else
504 {
505 /* Create the window. */
506 pCtx->hwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
507 gachWindowClassName, gachWindowClassName,
508 WS_POPUPWINDOW,
509 -200, -200, 100, 100, NULL, NULL, pCtx->pEnv->hInstance, NULL);
510
511 if (pCtx->hwnd == NULL)
512 {
513 rc = VERR_NOT_SUPPORTED;
514 }
515 else
516 {
517 SetWindowPos(pCtx->hwnd, HWND_TOPMOST, -200, -200, 0, 0,
518 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
519
520 pCtx->hwndNextInChain = SetClipboardViewer (pCtx->hwnd);
521 }
522 }
523
524 Log(("vboxClipboardInit returned with rc = %Rrc\n", rc));
525 return rc;
526}
527
528static void vboxClipboardDestroy(VBOXCLIPBOARDCONTEXT *pCtx)
529{
530 if (pCtx->hwnd)
531 {
532 ChangeClipboardChain (pCtx->hwnd, pCtx->hwndNextInChain);
533 pCtx->hwndNextInChain = NULL;
534
535 DestroyWindow (pCtx->hwnd);
536 pCtx->hwnd = NULL;
537 }
538
539 if (pCtx->atomWindowClass != 0)
540 {
541 UnregisterClass(gachWindowClassName, pCtx->pEnv->hInstance);
542 pCtx->atomWindowClass = 0;
543 }
544}
545
546/* Static since it is the single instance. Directly used in the windows proc. */
547static VBOXCLIPBOARDCONTEXT gCtx = { NULL };
548
549static LRESULT CALLBACK vboxClipboardWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
550{
551 /* Forward with proper context. */
552 return vboxClipboardProcessMsg(&gCtx, hwnd, msg, wParam, lParam);
553}
554
555int VBoxClipboardInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread)
556{
557 Log(("VboxClipboardInit\n"));
558 if (gCtx.pEnv)
559 {
560 /* Clipboard was already initialized. 2 or more instances are not supported. */
561 return VERR_NOT_SUPPORTED;
562 }
563
564 RT_ZERO (gCtx);
565 gCtx.pEnv = pEnv;
566
567 int rc = VbglR3ClipboardConnect(&gCtx.u32ClientID);
568 if (RT_SUCCESS (rc))
569 {
570 rc = vboxClipboardInit(&gCtx);
571 if (RT_SUCCESS (rc))
572 {
573 /* Always start the thread for host messages. */
574 *pfStartThread = true;
575 }
576 else
577 {
578 VbglR3ClipboardDisconnect(gCtx.u32ClientID);
579 }
580 }
581
582 if (RT_SUCCESS(rc))
583 *ppInstance = &gCtx;
584 return rc;
585}
586
587unsigned __stdcall VBoxClipboardThread(void *pInstance)
588{
589 Log(("VBoxClipboardThread\n"));
590
591 VBOXCLIPBOARDCONTEXT *pCtx = (VBOXCLIPBOARDCONTEXT *)pInstance;
592 AssertPtr(pCtx);
593
594 /* The thread waits for incoming messages from the host. */
595 for (;;)
596 {
597 uint32_t u32Msg;
598 uint32_t u32Formats;
599 int rc = VbglR3ClipboardGetHostMsg(pCtx->u32ClientID, &u32Msg, &u32Formats);
600 if (RT_FAILURE(rc))
601 {
602 Log(("VBoxClipboardThread: Failed to call the driver for host message! rc = %Rrc\n", rc));
603 if (rc == VERR_INTERRUPTED)
604 {
605 /* Wait for termination event. */
606 WaitForSingleObject(pCtx->pEnv->hStopEvent, INFINITE);
607 break;
608 }
609 /* Wait a bit before retrying. */
610 AssertPtr(pCtx->pEnv);
611 if (WaitForSingleObject(pCtx->pEnv->hStopEvent, 1000) == WAIT_OBJECT_0)
612 {
613 break;
614 }
615 continue;
616 }
617 else
618 {
619 Log(("VBoxClipboardThread: VbglR3ClipboardGetHostMsg u32Msg = %ld, u32Formats = %ld\n", u32Msg, u32Formats));
620 switch (u32Msg)
621 {
622 case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
623 {
624 /* The host has announced available clipboard formats.
625 * Forward the information to the window, so it can later
626 * respond to WM_RENDERFORMAT message. */
627 ::PostMessage (pCtx->hwnd, WM_USER, 0, u32Formats);
628 } break;
629
630 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
631 {
632 /* The host needs data in the specified format. */
633 ::PostMessage (pCtx->hwnd, WM_USER + 1, 0, u32Formats);
634 } break;
635
636 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
637 {
638 /* The host is terminating. */
639 rc = VERR_INTERRUPTED;
640 } break;
641
642 default:
643 {
644 Log(("VBoxClipboardThread: Unsupported message from host! Message = %ld\n", u32Msg));
645 }
646 }
647 }
648 }
649 return 0;
650}
651
652void VBoxClipboardDestroy(const VBOXSERVICEENV *pEnv, void *pInstance)
653{
654 VBOXCLIPBOARDCONTEXT *pCtx = (VBOXCLIPBOARDCONTEXT *)pInstance;
655 if (pCtx != &gCtx)
656 {
657 Log(("VBoxClipboardDestroy: invalid instance %p (our = %p)!\n", pCtx, &gCtx));
658 pCtx = &gCtx;
659 }
660
661 vboxClipboardDestroy (pCtx);
662 VbglR3ClipboardDisconnect(pCtx->u32ClientID);
663 memset (pCtx, 0, sizeof (*pCtx));
664 return;
665}
666
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