VirtualBox

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

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

*: scm --update-copyright-year

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