VirtualBox

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

Last change on this file since 61361 was 60784, checked in by vboxsync, 9 years ago

SharedClipboard: vboxOpenClipboard cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.7 KB
Line 
1/* $Id: VBoxClipboard.cpp 60784 2016-05-02 11:41:08Z 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
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 default:
662 {
663 rc = DefWindowProc(hwnd, msg, wParam, lParam);
664 }
665 }
666
667#ifndef DEBUG_andy
668 LogFlowFunc(("vboxClipboardProcessMsg returned with rc = %ld\n", rc));
669#endif
670 return rc;
671}
672
673static LRESULT CALLBACK vboxClipboardWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
674
675static int vboxClipboardCreateWindow(PVBOXCLIPBOARDCONTEXT pCtx)
676{
677 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
678
679 int rc = VINF_SUCCESS;
680
681 AssertPtr(pCtx->pEnv);
682 HINSTANCE hInstance = pCtx->pEnv->hInstance;
683 Assert(hInstance != 0);
684
685 /* Register the Window Class. */
686 WNDCLASSEX wc = { 0 };
687 wc.cbSize = sizeof(WNDCLASSEX);
688
689 if (!GetClassInfoEx(hInstance, s_szClipWndClassName, &wc))
690 {
691 wc.style = CS_NOCLOSE;
692 wc.lpfnWndProc = vboxClipboardWndProc;
693 wc.hInstance = pCtx->pEnv->hInstance;
694 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
695 wc.lpszClassName = s_szClipWndClassName;
696
697 pCtx->wndClass = RegisterClassEx(&wc);
698 if (pCtx->wndClass == 0)
699 rc = RTErrConvertFromWin32(GetLastError());
700 }
701
702 if (RT_SUCCESS(rc))
703 {
704 /* Create the window. */
705 pCtx->hwnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
706 s_szClipWndClassName, s_szClipWndClassName,
707 WS_POPUPWINDOW,
708 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
709 if (pCtx->hwnd == NULL)
710 {
711 rc = VERR_NOT_SUPPORTED;
712 }
713 else
714 {
715 SetWindowPos(pCtx->hwnd, HWND_TOPMOST, -200, -200, 0, 0,
716 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
717
718 vboxClipboardAddToCBChain(pCtx);
719 if (!vboxClipboardIsNewAPI(pCtx))
720 pCtx->timerRefresh = SetTimer(pCtx->hwnd, 0, 10 * 1000, NULL);
721 }
722 }
723
724 LogFlowFuncLeaveRC(rc);
725 return rc;
726}
727
728static void vboxClipboardDestroy(PVBOXCLIPBOARDCONTEXT pCtx)
729{
730 AssertPtrReturnVoid(pCtx);
731
732 if (pCtx->hwnd)
733 {
734 vboxClipboardRemoveFromCBChain(pCtx);
735 if (pCtx->timerRefresh)
736 KillTimer(pCtx->hwnd, 0);
737
738 DestroyWindow(pCtx->hwnd);
739 pCtx->hwnd = NULL;
740 }
741
742 if (pCtx->wndClass != 0)
743 {
744 UnregisterClass(s_szClipWndClassName, pCtx->pEnv->hInstance);
745 pCtx->wndClass = 0;
746 }
747}
748
749static LRESULT CALLBACK vboxClipboardWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
750{
751 PVBOXCLIPBOARDCONTEXT pCtx = &g_Ctx; /** @todo r=andy Make pCtx available through SetWindowLongPtr() / GWL_USERDATA. */
752 AssertPtr(pCtx);
753
754 /* Forward with proper context. */
755 return vboxClipboardProcessMsg(pCtx, hWnd, uMsg, wParam, lParam);
756}
757
758DECLCALLBACK(int) VBoxClipboardInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
759{
760 LogFlowFuncEnter();
761
762 PVBOXCLIPBOARDCONTEXT pCtx = &g_Ctx; /* Only one instance for now. */
763 AssertPtr(pCtx);
764
765 if (pCtx->pEnv)
766 {
767 /* Clipboard was already initialized. 2 or more instances are not supported. */
768 return VERR_NOT_SUPPORTED;
769 }
770
771 if (VbglR3AutoLogonIsRemoteSession())
772 {
773 /* Do not use clipboard for remote sessions. */
774 LogRel(("Clipboard: Clipboard has been disabled for a remote session\n"));
775 return VERR_NOT_SUPPORTED;
776 }
777
778 RT_BZERO(pCtx, sizeof(VBOXCLIPBOARDCONTEXT));
779 pCtx->pEnv = pEnv;
780
781 /* Check that new Clipboard API is available */
782 vboxClipboardInitNewAPI(pCtx);
783
784 int rc = VbglR3ClipboardConnect(&pCtx->u32ClientID);
785 if (RT_SUCCESS(rc))
786 {
787 rc = vboxClipboardCreateWindow(pCtx);
788 if (RT_SUCCESS(rc))
789 {
790 *ppInstance = pCtx;
791 }
792 else
793 {
794 VbglR3ClipboardDisconnect(pCtx->u32ClientID);
795 }
796 }
797
798 LogFlowFuncLeaveRC(rc);
799 return rc;
800}
801
802DECLCALLBACK(int) VBoxClipboardWorker(void *pInstance, bool volatile *pfShutdown)
803{
804 AssertPtr(pInstance);
805 LogFlowFunc(("pInstance=%p\n", pInstance));
806
807 /*
808 * Tell the control thread that it can continue
809 * spawning services.
810 */
811 RTThreadUserSignal(RTThreadSelf());
812
813 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
814 AssertPtr(pCtx);
815
816 int rc;
817
818 /* The thread waits for incoming messages from the host. */
819 for (;;)
820 {
821 uint32_t u32Msg;
822 uint32_t u32Formats;
823 rc = VbglR3ClipboardGetHostMsg(pCtx->u32ClientID, &u32Msg, &u32Formats);
824 if (RT_FAILURE(rc))
825 {
826 if (rc == VERR_INTERRUPTED)
827 break;
828
829 LogFlowFunc(("Error getting host message, rc=%Rrc\n", rc));
830
831 if (*pfShutdown)
832 break;
833
834 /* Wait a bit before retrying. */
835 RTThreadSleep(1000);
836 continue;
837 }
838 else
839 {
840 LogFlowFunc(("u32Msg=%RU32, u32Formats=0x%x\n", u32Msg, u32Formats));
841 switch (u32Msg)
842 {
843 /** @todo r=andy: Use a \#define for WM_USER (+1). */
844 case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS:
845 {
846 /* The host has announced available clipboard formats.
847 * Forward the information to the window, so it can later
848 * respond to WM_RENDERFORMAT message. */
849 ::PostMessage(pCtx->hwnd, WM_USER, 0, u32Formats);
850 } break;
851
852 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
853 {
854 /* The host needs data in the specified format. */
855 ::PostMessage(pCtx->hwnd, WM_USER + 1, 0, u32Formats);
856 } break;
857
858 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
859 {
860 /* The host is terminating. */
861 LogRel(("Clipboard: Terminating ...\n"));
862 ASMAtomicXchgBool(pfShutdown, true);
863 } break;
864
865 default:
866 {
867 LogFlowFunc(("Unsupported message from host, message=%RU32\n", u32Msg));
868
869 /* Wait a bit before retrying. */
870 RTThreadSleep(1000);
871 } break;
872 }
873 }
874
875 if (*pfShutdown)
876 break;
877 }
878
879 LogFlowFuncLeaveRC(rc);
880 return rc;
881}
882
883DECLCALLBACK(int) VBoxClipboardStop(void *pInstance)
884{
885 AssertPtrReturn(pInstance, VERR_INVALID_POINTER);
886
887 LogFunc(("Stopping pInstance=%p\n", pInstance));
888
889 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
890 AssertPtr(pCtx);
891
892 VbglR3ClipboardDisconnect(pCtx->u32ClientID);
893 pCtx->u32ClientID = 0;
894
895 LogFlowFuncLeaveRC(VINF_SUCCESS);
896 return VINF_SUCCESS;
897}
898
899DECLCALLBACK(void) VBoxClipboardDestroy(void *pInstance)
900{
901 AssertPtrReturnVoid(pInstance);
902
903 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
904 AssertPtr(pCtx);
905
906 /* Make sure that we are disconnected. */
907 Assert(pCtx->u32ClientID == 0);
908
909 vboxClipboardDestroy(pCtx);
910 RT_BZERO(pCtx, sizeof(VBOXCLIPBOARDCONTEXT));
911
912 return;
913}
914
915/**
916 * The service description.
917 */
918VBOXSERVICEDESC g_SvcDescClipboard =
919{
920 /* pszName. */
921 "clipboard",
922 /* pszDescription. */
923 "Shared Clipboard",
924 /* methods */
925 VBoxClipboardInit,
926 VBoxClipboardWorker,
927 VBoxClipboardStop,
928 VBoxClipboardDestroy
929};
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