VirtualBox

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

Last change on this file since 69360 was 68630, checked in by vboxsync, 7 years ago

Adding VBoxGuestCoreTypes.h for avoiding having to include VMMDev.h from VBoxGuestLib.h. Dropped a few unnecessary VMMDev.h includes here and there.

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