VirtualBox

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

Last change on this file since 76748 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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