VirtualBox

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

Last change on this file since 58832 was 58604, checked in by vboxsync, 9 years ago

bugref:6466: Additions/SharedClipboard: Added support of new MS clipboard API

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.6 KB
Line 
1/* $Id: VBoxClipboard.cpp 58604 2015-11-06 12:59:50Z vboxsync $ */
2/** @file
3 * VBoxClipboard - Shared clipboard, Windows Guest Implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "VBoxTray.h"
19#include "VBoxHelpers.h"
20
21#include <iprt/asm.h>
22#include <iprt/ldr.h>
23
24#include <VBox/HostServices/VBoxClipboardSvc.h>
25#include <strsafe.h>
26
27#include <VBox/VMMDev.h>
28#ifdef DEBUG
29# define LOG_ENABLED
30# define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
31#endif
32#include <VBox/log.h>
33
34 /* Dynamically load clipboard functions from User32.dll. */
35typedef BOOL WINAPI FNADDCLIPBOARDFORMATLISTENER(HWND);
36typedef FNADDCLIPBOARDFORMATLISTENER *PFNADDCLIPBOARDFORMATLISTENER;
37
38typedef BOOL WINAPI FNREMOVECLIPBOARDFORMATLISTENER(HWND);
39typedef FNREMOVECLIPBOARDFORMATLISTENER *PFNREMOVECLIPBOARDFORMATLISTENER;
40
41#ifndef WM_CLIPBOARDUPDATE
42#define WM_CLIPBOARDUPDATE 0x031D
43#endif
44
45typedef struct _VBOXCLIPBOARDCONTEXT
46{
47 const VBOXSERVICEENV *pEnv;
48 uint32_t u32ClientID;
49 ATOM wndClass;
50 HWND hwnd;
51 HWND hwndNextInChain;
52 UINT timerRefresh;
53 bool fCBChainPingInProcess;
54 PFNADDCLIPBOARDFORMATLISTENER pfnAddClipboardFormatListener;
55 PFNREMOVECLIPBOARDFORMATLISTENER pfnRemoveClipboardFormatListener;
56} VBOXCLIPBOARDCONTEXT, *PVBOXCLIPBOARDCONTEXT;
57
58/** Static since it is the single instance. Directly used in the windows proc. */
59static VBOXCLIPBOARDCONTEXT g_Ctx = { NULL };
60
61static char s_szClipWndClassName[] = "VBoxSharedClipboardClass";
62
63enum { CBCHAIN_TIMEOUT = 5000 /* ms */ };
64
65static void vboxClipboardInitNewAPI(VBOXCLIPBOARDCONTEXT *pCtx)
66{
67 RTLDRMOD hUser32 = NIL_RTLDRMOD;
68 int rc = RTLdrLoadSystem("User32.dll", /* fNoUnload = */ true, &hUser32);
69 if (RT_SUCCESS(rc))
70 {
71 rc = RTLdrGetSymbol(hUser32, "AddClipboardFormatListener", (void**)&pCtx->pfnAddClipboardFormatListener);
72 if (RT_SUCCESS(rc))
73 {
74 rc = RTLdrGetSymbol(hUser32, "RemoveClipboardFormatListener", (void**)&pCtx->pfnRemoveClipboardFormatListener);
75 }
76
77 RTLdrClose(hUser32);
78 }
79
80 if (RT_SUCCESS(rc))
81 {
82 Log(("New Clipboard API is enabled\n"));
83 }
84 else
85 {
86 pCtx->pfnAddClipboardFormatListener = NULL;
87 pCtx->pfnRemoveClipboardFormatListener = NULL;
88 Log(("New Clipboard API is not available. rc = %Rrc\n", rc));
89 }
90}
91
92static bool vboxClipboardIsNewAPI(VBOXCLIPBOARDCONTEXT *pCtx)
93{
94 return pCtx->pfnAddClipboardFormatListener != NULL;
95}
96
97static int vboxClipboardChanged(PVBOXCLIPBOARDCONTEXT pCtx)
98{
99 AssertPtr(pCtx);
100
101 /* Query list of available formats and report to host. */
102 int rc = VINF_SUCCESS;
103 if (FALSE == OpenClipboard(pCtx->hwnd))
104 {
105 rc = RTErrConvertFromWin32(GetLastError());
106 }
107 else
108 {
109 uint32_t u32Formats = 0;
110 UINT format = 0;
111
112 while ((format = EnumClipboardFormats(format)) != 0)
113 {
114 LogFlowFunc(("vboxClipboardChanged: format = 0x%08X\n", format));
115 switch (format)
116 {
117 case CF_UNICODETEXT:
118 case CF_TEXT:
119 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
120 break;
121
122 case CF_DIB:
123 case CF_BITMAP:
124 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
125 break;
126
127 default:
128 {
129 if (format >= 0xC000)
130 {
131 TCHAR szFormatName[256];
132
133 int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
134 if (cActual)
135 {
136 if (strcmp (szFormatName, "HTML Format") == 0)
137 {
138 u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
139 }
140 }
141 }
142 break;
143 }
144 }
145 }
146
147 CloseClipboard();
148 rc = VbglR3ClipboardReportFormats(pCtx->u32ClientID, u32Formats);
149 }
150 return rc;
151}
152
153/* Add ourselves into the chain of cliboard listeners */
154static void vboxClipboardAddToCBChain(PVBOXCLIPBOARDCONTEXT pCtx)
155{
156 AssertPtrReturnVoid(pCtx);
157 if (vboxClipboardIsNewAPI(pCtx))
158 pCtx->pfnAddClipboardFormatListener(pCtx->hwnd);
159 else
160 pCtx->hwndNextInChain = SetClipboardViewer(pCtx->hwnd);
161 /** @todo r=andy Return code?? */
162}
163
164/* Remove ourselves from the chain of cliboard listeners */
165static void vboxClipboardRemoveFromCBChain(PVBOXCLIPBOARDCONTEXT pCtx)
166{
167 AssertPtrReturnVoid(pCtx);
168
169 if (vboxClipboardIsNewAPI(pCtx))
170 {
171 pCtx->pfnRemoveClipboardFormatListener(pCtx->hwnd);
172 }
173 else
174 {
175 ChangeClipboardChain(pCtx->hwnd, pCtx->hwndNextInChain);
176 pCtx->hwndNextInChain = NULL;
177 }
178 /** @todo r=andy Return code?? */
179}
180
181/* Callback which is invoked when we have successfully pinged ourselves down the
182 * clipboard chain. We simply unset a boolean flag to say that we are responding.
183 * There is a race if a ping returns after the next one is initiated, but nothing
184 * very bad is likely to happen. */
185VOID CALLBACK vboxClipboardChainPingProc(HWND hWnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult)
186{
187 NOREF(hWnd);
188 NOREF(uMsg);
189 NOREF(lResult);
190
191 /** @todo r=andy Why not using SetWindowLongPtr for keeping the context? */
192 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)dwData;
193 AssertPtr(pCtx);
194
195 pCtx->fCBChainPingInProcess = FALSE;
196}
197
198static LRESULT vboxClipboardProcessMsg(PVBOXCLIPBOARDCONTEXT pCtx, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
199{
200 AssertPtr(pCtx);
201
202 LRESULT rc = 0;
203
204 switch (msg)
205 {
206 case WM_CLIPBOARDUPDATE:
207 {
208 Log(("WM_CLIPBOARDUPDATE\n"));
209
210 if (GetClipboardOwner() != hwnd)
211 {
212 /* Clipboard was updated by another application. */
213 vboxClipboardChanged(pCtx);
214 }
215 } break;
216
217 case WM_CHANGECBCHAIN:
218 {
219 if (vboxClipboardIsNewAPI(pCtx))
220 {
221 rc = DefWindowProc(hwnd, msg, wParam, lParam);
222 break;
223 }
224
225 HWND hwndRemoved = (HWND)wParam;
226 HWND hwndNext = (HWND)lParam;
227
228 LogFlowFunc(("WM_CHANGECBCHAIN: hwndRemoved %p, hwndNext %p, hwnd %p\n", hwndRemoved, hwndNext, pCtx->hwnd));
229
230 if (hwndRemoved == pCtx->hwndNextInChain)
231 {
232 /* The window that was next to our in the chain is being removed.
233 * Relink to the new next window. */
234 pCtx->hwndNextInChain = hwndNext;
235 }
236 else
237 {
238 if (pCtx->hwndNextInChain)
239 {
240 /* Pass the message further. */
241 DWORD_PTR dwResult;
242 rc = SendMessageTimeout(pCtx->hwndNextInChain, WM_CHANGECBCHAIN, wParam, lParam, 0, CBCHAIN_TIMEOUT, &dwResult);
243 if (!rc)
244 rc = (LRESULT) dwResult;
245 }
246 }
247 } break;
248
249 case WM_DRAWCLIPBOARD:
250 {
251 LogFlowFunc(("WM_DRAWCLIPBOARD, hwnd %p\n", pCtx->hwnd));
252
253 if (GetClipboardOwner() != hwnd)
254 {
255 /* Clipboard was updated by another application. */
256 /* WM_DRAWCLIPBOARD always expects a return code of 0, so don't change "rc" here. */
257 int vboxrc = vboxClipboardChanged(pCtx);
258 if (RT_FAILURE(vboxrc))
259 LogFlowFunc(("vboxClipboardChanged failed, rc = %Rrc\n", vboxrc));
260 }
261
262 if (pCtx->hwndNextInChain)
263 {
264 /* Pass the message to next windows in the clipboard chain. */
265 SendMessageTimeout(pCtx->hwndNextInChain, msg, wParam, lParam, 0, CBCHAIN_TIMEOUT, NULL);
266 }
267 } break;
268
269 case WM_TIMER:
270 {
271 if (vboxClipboardIsNewAPI(pCtx))
272 break;
273
274 HWND hViewer = GetClipboardViewer();
275
276 /* Re-register ourselves in the clipboard chain if our last ping
277 * timed out or there seems to be no valid chain. */
278 if (!hViewer || pCtx->fCBChainPingInProcess)
279 {
280 vboxClipboardRemoveFromCBChain(pCtx);
281 vboxClipboardAddToCBChain(pCtx);
282 }
283 /* Start a new ping by passing a dummy WM_CHANGECBCHAIN to be
284 * processed by ourselves to the chain. */
285 pCtx->fCBChainPingInProcess = TRUE;
286 hViewer = GetClipboardViewer();
287 if (hViewer)
288 SendMessageCallback(hViewer, WM_CHANGECBCHAIN, (WPARAM)pCtx->hwndNextInChain, (LPARAM)pCtx->hwndNextInChain, vboxClipboardChainPingProc, (ULONG_PTR)pCtx);
289 } break;
290
291 case WM_CLOSE:
292 {
293 /* Do nothing. Ignore the message. */
294 } break;
295
296 case WM_RENDERFORMAT:
297 {
298 /* Insert the requested clipboard format data into the clipboard. */
299 uint32_t u32Format = 0;
300 UINT format = (UINT)wParam;
301
302 LogFlowFunc(("WM_RENDERFORMAT, format = %x\n", format));
303 switch (format)
304 {
305 case CF_UNICODETEXT:
306 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
307 break;
308
309 case CF_DIB:
310 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
311 break;
312
313 default:
314 if (format >= 0xC000)
315 {
316 TCHAR szFormatName[256];
317
318 int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
319 if (cActual)
320 {
321 if (strcmp (szFormatName, "HTML Format") == 0)
322 {
323 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
324 }
325 }
326 }
327 break;
328 }
329
330 if (u32Format == 0)
331 {
332 /* Unsupported clipboard format is requested. */
333 LogFlowFunc(("Unsupported clipboard format requested: %ld\n", u32Format));
334 EmptyClipboard();
335 }
336 else
337 {
338 const uint32_t cbPrealloc = 4096; /* @todo r=andy Make it dynamic for supporting larger text buffers! */
339 uint32_t cb = 0;
340
341 /* Preallocate a buffer, most of small text transfers will fit into it. */
342 HANDLE hMem = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, cbPrealloc);
343 LogFlowFunc(("Preallocated handle hMem = %p\n", hMem));
344
345 if (hMem)
346 {
347 void *pMem = GlobalLock(hMem);
348 LogFlowFunc(("Locked pMem = %p, GlobalSize = %ld\n", pMem, GlobalSize(hMem)));
349
350 if (pMem)
351 {
352 /* Read the host data to the preallocated buffer. */
353 int vboxrc = VbglR3ClipboardReadData(pCtx->u32ClientID, u32Format, pMem, cbPrealloc, &cb);
354 LogFlowFunc(("VbglR3ClipboardReadData returned with rc = %Rrc\n", vboxrc));
355
356 if (RT_SUCCESS(vboxrc))
357 {
358 if (cb == 0)
359 {
360 /* 0 bytes returned means the clipboard is empty.
361 * Deallocate the memory and set hMem to NULL to get to
362 * the clipboard empty code path. */
363 GlobalUnlock(hMem);
364 GlobalFree(hMem);
365 hMem = NULL;
366 }
367 else if (cb > cbPrealloc)
368 {
369 GlobalUnlock(hMem);
370
371 /* The preallocated buffer is too small, adjust the size. */
372 hMem = GlobalReAlloc(hMem, cb, 0);
373 LogFlowFunc(("Reallocated hMem = %p\n", hMem));
374
375 if (hMem)
376 {
377 pMem = GlobalLock(hMem);
378 LogFlowFunc(("Locked pMem = %p, GlobalSize = %ld\n", pMem, GlobalSize(hMem)));
379
380 if (pMem)
381 {
382 /* Read the host data to the preallocated buffer. */
383 uint32_t cbNew = 0;
384 vboxrc = VbglR3ClipboardReadData(pCtx->u32ClientID, u32Format, pMem, cb, &cbNew);
385 LogFlowFunc(("VbglR3ClipboardReadData returned with rc = %Rrc, cb = %d, cbNew = %d\n", vboxrc, cb, cbNew));
386
387 if (RT_SUCCESS (vboxrc) && cbNew <= cb)
388 {
389 cb = cbNew;
390 }
391 else
392 {
393 GlobalUnlock(hMem);
394 GlobalFree(hMem);
395 hMem = NULL;
396 }
397 }
398 else
399 {
400 GlobalFree(hMem);
401 hMem = NULL;
402 }
403 }
404 }
405
406 if (hMem)
407 {
408 /* pMem is the address of the data. cb is the size of returned data. */
409 /* Verify the size of returned text, the memory block for clipboard
410 * must have the exact string size.
411 */
412 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
413 {
414 size_t cbActual = 0;
415 HRESULT hrc = StringCbLengthW((LPWSTR)pMem, cb, &cbActual);
416 if (FAILED (hrc))
417 {
418 /* Discard invalid data. */
419 GlobalUnlock(hMem);
420 GlobalFree(hMem);
421 hMem = NULL;
422 }
423 else
424 {
425 /* cbActual is the number of bytes, excluding those used
426 * for the terminating null character.
427 */
428 cb = (uint32_t)(cbActual + 2);
429 }
430 }
431 }
432
433 if (hMem)
434 {
435 GlobalUnlock(hMem);
436
437 hMem = GlobalReAlloc(hMem, cb, 0);
438 LogFlowFunc(("Reallocated hMem = %p\n", hMem));
439
440 if (hMem)
441 {
442 /* 'hMem' contains the host clipboard data.
443 * size is 'cb' and format is 'format'. */
444 HANDLE hClip = SetClipboardData(format, hMem);
445 LogFlowFunc(("WM_RENDERFORMAT hClip = %p\n", hClip));
446
447 if (hClip)
448 {
449 /* The hMem ownership has gone to the system. Finish the processing. */
450 break;
451 }
452
453 /* Cleanup follows. */
454 }
455 }
456 }
457 if (hMem)
458 GlobalUnlock(hMem);
459 }
460 if (hMem)
461 GlobalFree(hMem);
462 }
463
464 /* Something went wrong. */
465 EmptyClipboard();
466 }
467 } break;
468
469 case WM_RENDERALLFORMATS:
470 {
471 /* Do nothing. The clipboard formats will be unavailable now, because the
472 * windows is to be destroyed and therefore the guest side becomes inactive.
473 */
474 if (OpenClipboard(hwnd))
475 {
476 EmptyClipboard();
477 CloseClipboard();
478 }
479 } break;
480
481 case WM_USER:
482 {
483 /* Announce available formats. Do not insert data, they will be inserted in WM_RENDER*. */
484 uint32_t u32Formats = (uint32_t)lParam;
485
486 if (FALSE == OpenClipboard(hwnd))
487 {
488 LogFlowFunc(("WM_USER: Failed to open clipboard! Last error = %ld\n", GetLastError()));
489 }
490 else
491 {
492 EmptyClipboard();
493
494 HANDLE hClip = NULL;
495
496 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
497 {
498 LogFlowFunc(("WM_USER: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT\n"));
499 hClip = SetClipboardData(CF_UNICODETEXT, NULL);
500 }
501
502 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
503 {
504 LogFlowFunc(("WM_USER: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
505 hClip = SetClipboardData(CF_DIB, NULL);
506 }
507
508 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
509 {
510 UINT format = RegisterClipboardFormat ("HTML Format");
511 LogFlowFunc(("WM_USER: VBOX_SHARED_CLIPBOARD_FMT_HTML 0x%04X\n", format));
512 if (format != 0)
513 {
514 hClip = SetClipboardData(format, NULL);
515 }
516 }
517
518 CloseClipboard();
519 LogFlowFunc(("WM_USER: hClip = %p, err = %ld\n", hClip, GetLastError ()));
520 }
521 } break;
522
523 case WM_USER + 1:
524 {
525 /* Send data in the specified format to the host. */
526 uint32_t u32Formats = (uint32_t)lParam;
527 HANDLE hClip = NULL;
528
529 if (FALSE == OpenClipboard(hwnd))
530 {
531 LogFlowFunc(("WM_USER: Failed to open clipboard! Last error = %ld\n", GetLastError()));
532 }
533 else
534 {
535 int vboxrc;
536 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
537 {
538 hClip = GetClipboardData(CF_DIB);
539
540 if (hClip != NULL)
541 {
542 LPVOID lp = GlobalLock(hClip);
543 if (lp != NULL)
544 {
545 LogFlowFunc(("WM_USER + 1: CF_DIB\n"));
546 vboxrc = VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_BITMAP,
547 lp, GlobalSize(hClip));
548 GlobalUnlock(hClip);
549 }
550 else
551 {
552 hClip = NULL;
553 }
554 }
555 }
556 else if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
557 {
558 hClip = GetClipboardData(CF_UNICODETEXT);
559
560 if (hClip != NULL)
561 {
562 LPWSTR uniString = (LPWSTR)GlobalLock(hClip);
563
564 if (uniString != NULL)
565 {
566 LogFlowFunc(("WM_USER + 1: CF_UNICODETEXT\n"));
567 vboxrc = VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
568 uniString, (lstrlenW(uniString) + 1) * 2);
569 GlobalUnlock(hClip);
570 }
571 else
572 {
573 hClip = NULL;
574 }
575 }
576 }
577 else if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
578 {
579 UINT format = RegisterClipboardFormat ("HTML Format");
580 if (format != 0)
581 {
582 hClip = GetClipboardData(format);
583 if (hClip != NULL)
584 {
585 LPVOID lp = GlobalLock(hClip);
586
587 if (lp != NULL)
588 {
589 LogFlowFunc(("WM_USER + 1: CF_HTML\n"));
590 vboxrc = VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_HTML,
591 lp, GlobalSize(hClip));
592 GlobalUnlock(hClip);
593 }
594 else
595 {
596 hClip = NULL;
597 }
598 }
599 }
600 }
601
602 CloseClipboard();
603 }
604
605 if (hClip == NULL)
606 {
607 /* Requested clipboard format is not available, send empty data. */
608 VbglR3ClipboardWriteData(pCtx->u32ClientID, 0, NULL, 0);
609 }
610 } break;
611
612 default:
613 {
614 rc = DefWindowProc(hwnd, msg, wParam, lParam);
615 }
616 }
617
618#ifndef DEBUG_andy
619 LogFlowFunc(("vboxClipboardProcessMsg returned with rc = %ld\n", rc));
620#endif
621 return rc;
622}
623
624static LRESULT CALLBACK vboxClipboardWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
625
626static int vboxClipboardCreateWindow(PVBOXCLIPBOARDCONTEXT pCtx)
627{
628 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
629
630 int rc = VINF_SUCCESS;
631
632 AssertPtr(pCtx->pEnv);
633 HINSTANCE hInstance = pCtx->pEnv->hInstance;
634 Assert(hInstance != 0);
635
636 /* Register the Window Class. */
637 WNDCLASSEX wc = { 0 };
638 wc.cbSize = sizeof(WNDCLASSEX);
639
640 if (!GetClassInfoEx(hInstance, s_szClipWndClassName, &wc))
641 {
642 wc.style = CS_NOCLOSE;
643 wc.lpfnWndProc = vboxClipboardWndProc;
644 wc.hInstance = pCtx->pEnv->hInstance;
645 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
646 wc.lpszClassName = s_szClipWndClassName;
647
648 pCtx->wndClass = RegisterClassEx(&wc);
649 if (pCtx->wndClass == 0)
650 rc = RTErrConvertFromWin32(GetLastError());
651 }
652
653 if (RT_SUCCESS(rc))
654 {
655 /* Create the window. */
656 pCtx->hwnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
657 s_szClipWndClassName, s_szClipWndClassName,
658 WS_POPUPWINDOW,
659 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
660 if (pCtx->hwnd == NULL)
661 {
662 rc = VERR_NOT_SUPPORTED;
663 }
664 else
665 {
666 SetWindowPos(pCtx->hwnd, HWND_TOPMOST, -200, -200, 0, 0,
667 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
668
669 vboxClipboardAddToCBChain(pCtx);
670 if (!vboxClipboardIsNewAPI(pCtx))
671 pCtx->timerRefresh = SetTimer(pCtx->hwnd, 0, 10 * 1000, NULL);
672 }
673 }
674
675 LogFlowFuncLeaveRC(rc);
676 return rc;
677}
678
679static void vboxClipboardDestroy(PVBOXCLIPBOARDCONTEXT pCtx)
680{
681 AssertPtrReturnVoid(pCtx);
682
683 if (pCtx->hwnd)
684 {
685 vboxClipboardRemoveFromCBChain(pCtx);
686 if (pCtx->timerRefresh)
687 KillTimer(pCtx->hwnd, 0);
688
689 DestroyWindow(pCtx->hwnd);
690 pCtx->hwnd = NULL;
691 }
692
693 if (pCtx->wndClass != 0)
694 {
695 UnregisterClass(s_szClipWndClassName, pCtx->pEnv->hInstance);
696 pCtx->wndClass = 0;
697 }
698}
699
700static LRESULT CALLBACK vboxClipboardWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
701{
702 PVBOXCLIPBOARDCONTEXT pCtx = &g_Ctx; /** @todo r=andy Make pCtx available through SetWindowLongPtr() / GWL_USERDATA. */
703 AssertPtr(pCtx);
704
705 /* Forward with proper context. */
706 return vboxClipboardProcessMsg(pCtx, hWnd, uMsg, wParam, lParam);
707}
708
709DECLCALLBACK(int) VBoxClipboardInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
710{
711 LogFlowFuncEnter();
712
713 PVBOXCLIPBOARDCONTEXT pCtx = &g_Ctx; /* Only one instance for now. */
714 AssertPtr(pCtx);
715
716 if (pCtx->pEnv)
717 {
718 /* Clipboard was already initialized. 2 or more instances are not supported. */
719 return VERR_NOT_SUPPORTED;
720 }
721
722 if (VbglR3AutoLogonIsRemoteSession())
723 {
724 /* Do not use clipboard for remote sessions. */
725 LogRel(("Clipboard: Clipboard has been disabled for a remote session\n"));
726 return VERR_NOT_SUPPORTED;
727 }
728
729 RT_BZERO(pCtx, sizeof(VBOXCLIPBOARDCONTEXT));
730 pCtx->pEnv = pEnv;
731
732 /* Check that new Clipboard API is available */
733 vboxClipboardInitNewAPI(pCtx);
734
735 int rc = VbglR3ClipboardConnect(&pCtx->u32ClientID);
736 if (RT_SUCCESS(rc))
737 {
738 rc = vboxClipboardCreateWindow(pCtx);
739 if (RT_SUCCESS(rc))
740 {
741 *ppInstance = pCtx;
742 }
743 else
744 {
745 VbglR3ClipboardDisconnect(pCtx->u32ClientID);
746 }
747 }
748
749 LogFlowFuncLeaveRC(rc);
750 return rc;
751}
752
753DECLCALLBACK(int) VBoxClipboardWorker(void *pInstance, bool volatile *pfShutdown)
754{
755 AssertPtr(pInstance);
756 LogFlowFunc(("pInstance=%p\n", pInstance));
757
758 /*
759 * Tell the control thread that it can continue
760 * spawning services.
761 */
762 RTThreadUserSignal(RTThreadSelf());
763
764 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
765 AssertPtr(pCtx);
766
767 int rc;
768
769 /* The thread waits for incoming messages from the host. */
770 for (;;)
771 {
772 uint32_t u32Msg;
773 uint32_t u32Formats;
774 rc = VbglR3ClipboardGetHostMsg(pCtx->u32ClientID, &u32Msg, &u32Formats);
775 if (RT_FAILURE(rc))
776 {
777 if (rc == VERR_INTERRUPTED)
778 break;
779
780 LogFlowFunc(("Error getting host message, rc=%Rrc\n", rc));
781
782 if (*pfShutdown)
783 break;
784
785 /* Wait a bit before retrying. */
786 RTThreadSleep(1000);
787 continue;
788 }
789 else
790 {
791 LogFlowFunc(("u32Msg=%RU32, u32Formats=0x%x\n", u32Msg, u32Formats));
792 switch (u32Msg)
793 {
794 /** @todo r=andy: Use a \#define for WM_USER (+1). */
795 case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
796 {
797 /* The host has announced available clipboard formats.
798 * Forward the information to the window, so it can later
799 * respond to WM_RENDERFORMAT message. */
800 ::PostMessage(pCtx->hwnd, WM_USER, 0, u32Formats);
801 } break;
802
803 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
804 {
805 /* The host needs data in the specified format. */
806 ::PostMessage(pCtx->hwnd, WM_USER + 1, 0, u32Formats);
807 } break;
808
809 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
810 {
811 /* The host is terminating. */
812 LogRel(("Clipboard: Terminating ...\n"));
813 ASMAtomicXchgBool(pfShutdown, true);
814 } break;
815
816 default:
817 {
818 LogFlowFunc(("Unsupported message from host, message=%RU32\n", u32Msg));
819
820 /* Wait a bit before retrying. */
821 RTThreadSleep(1000);
822 } break;
823 }
824 }
825
826 if (*pfShutdown)
827 break;
828 }
829
830 LogFlowFuncLeaveRC(rc);
831 return rc;
832}
833
834DECLCALLBACK(int) VBoxClipboardStop(void *pInstance)
835{
836 AssertPtrReturn(pInstance, VERR_INVALID_POINTER);
837
838 LogFunc(("Stopping pInstance=%p\n", pInstance));
839
840 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
841 AssertPtr(pCtx);
842
843 VbglR3ClipboardDisconnect(pCtx->u32ClientID);
844 pCtx->u32ClientID = 0;
845
846 LogFlowFuncLeaveRC(VINF_SUCCESS);
847 return VINF_SUCCESS;
848}
849
850DECLCALLBACK(void) VBoxClipboardDestroy(void *pInstance)
851{
852 AssertPtrReturnVoid(pInstance);
853
854 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
855 AssertPtr(pCtx);
856
857 /* Make sure that we are disconnected. */
858 Assert(pCtx->u32ClientID == 0);
859
860 vboxClipboardDestroy(pCtx);
861 RT_BZERO(pCtx, sizeof(VBOXCLIPBOARDCONTEXT));
862
863 return;
864}
865
866/**
867 * The service description.
868 */
869VBOXSERVICEDESC g_SvcDescClipboard =
870{
871 /* pszName. */
872 "clipboard",
873 /* pszDescription. */
874 "Shared Clipboard",
875 /* methods */
876 VBoxClipboardInit,
877 VBoxClipboardWorker,
878 VBoxClipboardStop,
879 VBoxClipboardDestroy
880};
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