VirtualBox

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

Last change on this file since 63099 was 62522, checked in by vboxsync, 8 years ago

(C) 2016

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