VirtualBox

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

Last change on this file since 27992 was 26832, checked in by vboxsync, 15 years ago

VBoxTray/Clipboard: Build fix.

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