VirtualBox

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

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

Shared Clipboard/URI: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.8 KB
Line 
1/* $Id: VBoxClipboard.cpp 78809 2019-05-28 10:54: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/GuestHost/SharedClipboard.h>
30#include <VBox/GuestHost/SharedClipboard-win.h>
31#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
32# include <VBox/GuestHost/SharedClipboard-uri.h>
33#endif
34#include <strsafe.h>
35
36#include <VBox/log.h>
37
38
39#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
40/** !!! HACK ALERT !!! Dynamically resolve functions! */
41# ifdef _WIN32_IE
42# undef _WIN32_IE
43# define _WIN32_IE 0x0501
44# endif
45# include <iprt/win/shlobj.h>
46# include <iprt/win/shlwapi.h>
47#endif
48
49
50/*********************************************************************************************************************************
51* Structures and Typedefs *
52*********************************************************************************************************************************/
53
54struct _VBOXCLIPBOARDCONTEXT
55{
56 /** Pointer to the VBoxClient service environment. */
57 const VBOXSERVICEENV *pEnv;
58 /** Client ID the service is connected to the HGCM service with. */
59 uint32_t u32ClientID;
60 /** Windows-specific context data. */
61 VBOXCLIPBOARDWINCTX Win;
62};
63
64
65/*********************************************************************************************************************************
66* Static variables *
67*********************************************************************************************************************************/
68/** Static clipboard context (since it is the single instance). Directly used in the windows proc. */
69static VBOXCLIPBOARDCONTEXT g_Ctx = { NULL };
70/** Static window class name. */
71static char s_szClipWndClassName[] = VBOX_CLIPBOARD_WNDCLASS_NAME;
72
73
74
75static LRESULT vboxClipboardWinProcessMsg(PVBOXCLIPBOARDCONTEXT pCtx, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
76{
77 AssertPtr(pCtx);
78
79 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
80
81 LRESULT lresultRc = 0;
82
83 switch (msg)
84 {
85 case WM_CLIPBOARDUPDATE:
86 {
87 if (GetClipboardOwner() != hwnd)
88 {
89 /* Clipboard was updated by another application.
90 * Report available formats to the host. */
91 VBOXCLIPBOARDFORMATS fFormats;
92 int rc = VBoxClipboardWinGetFormats(&pCtx->Win, &fFormats);
93 if (RT_SUCCESS(rc))
94 rc = VbglR3ClipboardReportFormats(pCtx->u32ClientID, fFormats);
95
96 LogFunc(("WM_CLIPBOARDUPDATE: rc=%Rrc, fFormats=0x%x\n", rc, fFormats));
97 }
98 else
99 LogFlowFunc(("WM_CLIPBOARDUPDATE: No change (VBoxTray is owner)\n"));
100 }
101 break;
102
103 case WM_CHANGECBCHAIN:
104 {
105 if (VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI))
106 {
107 lresultRc = DefWindowProc(hwnd, msg, wParam, lParam);
108 break;
109 }
110
111 HWND hWndRemoved = (HWND)wParam;
112 HWND hWndNext = (HWND)lParam;
113
114 LogFlowFunc(("WM_CHANGECBCHAIN: hWndRemoved=%p, hWndNext=%p, hWnd=%p\n", hWndRemoved, hWndNext, pWinCtx->hWnd));
115
116 if (hWndRemoved == pWinCtx->hWndNextInChain)
117 {
118 /* The window that was next to our in the chain is being removed.
119 * Relink to the new next window. */
120 pWinCtx->hWndNextInChain = hWndNext;
121 }
122 else
123 {
124 if (pWinCtx->hWndNextInChain)
125 {
126 /* Pass the message further. */
127 DWORD_PTR dwResult;
128 lresultRc = SendMessageTimeout(pWinCtx->hWndNextInChain, WM_CHANGECBCHAIN, wParam, lParam, 0,
129 VBOX_CLIPBOARD_CBCHAIN_TIMEOUT_MS, &dwResult);
130 if (!lresultRc)
131 lresultRc = (LRESULT)dwResult;
132 }
133 }
134 }
135 break;
136
137 case WM_DRAWCLIPBOARD:
138 {
139 LogFlowFunc(("WM_DRAWCLIPBOARD, hwnd %p\n", pWinCtx->hWnd));
140
141 if (GetClipboardOwner() != hwnd)
142 {
143 /* Clipboard was updated by another application. */
144 /* WM_DRAWCLIPBOARD always expects a return code of 0, so don't change "rc" here. */
145 VBOXCLIPBOARDFORMATS fFormats;
146 int rc = VBoxClipboardWinGetFormats(pWinCtx, &fFormats);
147 if (RT_SUCCESS(rc))
148 rc = VbglR3ClipboardReportFormats(pCtx->u32ClientID, fFormats);
149 }
150
151 if (pWinCtx->hWndNextInChain)
152 {
153 /* Pass the message to next windows in the clipboard chain. */
154 SendMessageTimeout(pWinCtx->hWndNextInChain, msg, wParam, lParam, 0, VBOX_CLIPBOARD_CBCHAIN_TIMEOUT_MS, NULL);
155 }
156 }
157 break;
158
159 case WM_TIMER:
160 {
161 if (VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI))
162 break;
163
164 HWND hViewer = GetClipboardViewer();
165
166 /* Re-register ourselves in the clipboard chain if our last ping
167 * timed out or there seems to be no valid chain. */
168 if (!hViewer || pWinCtx->oldAPI.fCBChainPingInProcess)
169 {
170 VBoxClipboardWinRemoveFromCBChain(pWinCtx);
171 VBoxClipboardWinAddToCBChain(pWinCtx);
172 }
173
174 /* Start a new ping by passing a dummy WM_CHANGECBCHAIN to be
175 * processed by ourselves to the chain. */
176 pWinCtx->oldAPI.fCBChainPingInProcess = TRUE;
177
178 hViewer = GetClipboardViewer();
179 if (hViewer)
180 SendMessageCallback(hViewer, WM_CHANGECBCHAIN, (WPARAM)pWinCtx->hWndNextInChain, (LPARAM)pWinCtx->hWndNextInChain,
181 VBoxClipboardWinChainPingProc, (ULONG_PTR)pWinCtx);
182 }
183 break;
184
185 case WM_CLOSE:
186 {
187 /* Do nothing. Ignore the message. */
188 }
189 break;
190
191 case WM_RENDERFORMAT:
192 {
193 LogFunc(("WM_RENDERFORMAT\n"));
194
195 /* Insert the requested clipboard format data into the clipboard. */
196 const UINT cfFormat = (UINT)wParam;
197
198 const VBOXCLIPBOARDFORMAT fFormat = VBoxClipboardWinClipboardFormatToVBox(cfFormat);
199
200 LogFunc(("WM_RENDERFORMAT: cfFormat=%u -> fFormat=0x%x\n", cfFormat, fFormat));
201
202 if (fFormat == VBOX_SHARED_CLIPBOARD_FMT_NONE)
203 {
204 LogFunc(("WM_RENDERFORMAT: Unsupported format requested\n"));
205 VBoxClipboardWinClear();
206 }
207 else
208 {
209 const uint32_t cbPrealloc = _4K;
210 uint32_t cb = 0;
211
212 /* Preallocate a buffer, most of small text transfers will fit into it. */
213 HANDLE hMem = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, cbPrealloc);
214 LogFlowFunc(("Preallocated handle hMem = %p\n", hMem));
215
216 if (hMem)
217 {
218 void *pMem = GlobalLock(hMem);
219 LogFlowFunc(("Locked pMem = %p, GlobalSize = %ld\n", pMem, GlobalSize(hMem)));
220
221 if (pMem)
222 {
223 /* Read the host data to the preallocated buffer. */
224 int rc = VbglR3ClipboardReadData(pCtx->u32ClientID, fFormat, pMem, cbPrealloc, &cb);
225 LogFlowFunc(("VbglR3ClipboardReadData returned with rc = %Rrc\n", rc));
226
227 if (RT_SUCCESS(rc))
228 {
229 if (cb == 0)
230 {
231 /* 0 bytes returned means the clipboard is empty.
232 * Deallocate the memory and set hMem to NULL to get to
233 * the clipboard empty code path. */
234 GlobalUnlock(hMem);
235 GlobalFree(hMem);
236 hMem = NULL;
237 }
238 else if (cb > cbPrealloc)
239 {
240 GlobalUnlock(hMem);
241
242 /* The preallocated buffer is too small, adjust the size. */
243 hMem = GlobalReAlloc(hMem, cb, 0);
244 LogFlowFunc(("Reallocated hMem = %p\n", hMem));
245
246 if (hMem)
247 {
248 pMem = GlobalLock(hMem);
249 LogFlowFunc(("Locked pMem = %p, GlobalSize = %ld\n", pMem, GlobalSize(hMem)));
250
251 if (pMem)
252 {
253 /* Read the host data to the preallocated buffer. */
254 uint32_t cbNew = 0;
255 rc = VbglR3ClipboardReadData(pCtx->u32ClientID, fFormat, pMem, cb, &cbNew);
256 LogFlowFunc(("VbglR3ClipboardReadData returned with rc = %Rrc, cb = %d, cbNew = %d\n",
257 rc, cb, cbNew));
258
259 if (RT_SUCCESS(rc)
260 && cbNew <= cb)
261 {
262 cb = cbNew;
263 }
264 else
265 {
266 GlobalUnlock(hMem);
267 GlobalFree(hMem);
268 hMem = NULL;
269 }
270 }
271 else
272 {
273 GlobalFree(hMem);
274 hMem = NULL;
275 }
276 }
277 }
278
279 if (hMem)
280 {
281 /* pMem is the address of the data. cb is the size of returned data. */
282 /* Verify the size of returned text, the memory block for clipboard
283 * must have the exact string size.
284 */
285 if (fFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
286 {
287 size_t cbActual = 0;
288 HRESULT hrc = StringCbLengthW((LPWSTR)pMem, cb, &cbActual);
289 if (FAILED(hrc))
290 {
291 /* Discard invalid data. */
292 GlobalUnlock(hMem);
293 GlobalFree(hMem);
294 hMem = NULL;
295 }
296 else
297 {
298 /* cbActual is the number of bytes, excluding those used
299 * for the terminating null character.
300 */
301 cb = (uint32_t)(cbActual + 2);
302 }
303 }
304 }
305
306 if (hMem)
307 {
308 GlobalUnlock(hMem);
309
310 hMem = GlobalReAlloc(hMem, cb, 0);
311 LogFlowFunc(("Reallocated hMem = %p\n", hMem));
312
313 if (hMem)
314 {
315 /* 'hMem' contains the host clipboard data.
316 * size is 'cb' and format is 'format'. */
317 HANDLE hClip = SetClipboardData(cfFormat, hMem);
318 LogFlowFunc(("WM_RENDERFORMAT hClip = %p\n", hClip));
319
320 if (hClip)
321 {
322 /* The hMem ownership has gone to the system. Finish the processing. */
323 break;
324 }
325
326 /* Cleanup follows. */
327 }
328 }
329 }
330 if (hMem)
331 GlobalUnlock(hMem);
332 }
333 if (hMem)
334 GlobalFree(hMem);
335 }
336 }
337 }
338 break;
339
340 case WM_RENDERALLFORMATS:
341 {
342 /* Do nothing. The clipboard formats will be unavailable now, because the
343 * windows is to be destroyed and therefore the guest side becomes inactive.
344 */
345 int rc = VBoxClipboardWinOpen(hwnd);
346 if (RT_SUCCESS(rc))
347 {
348 VBoxClipboardWinClear();
349 VBoxClipboardWinClose();
350 }
351 }
352 break;
353
354 case VBOX_CLIPBOARD_WM_SET_FORMATS:
355 {
356 /* Announce available formats. Do not insert data, they will be inserted in WM_RENDER*. */
357 VBOXCLIPBOARDFORMATS fFormats = (uint32_t)lParam;
358
359 LogFunc(("VBOX_WM_SHCLPB_SET_FORMATS: fFormats=0x%x\n", fFormats));
360
361 int rc = VBoxClipboardWinOpen(hwnd);
362 if (RT_SUCCESS(rc))
363 {
364 VBoxClipboardWinClear();
365
366 HANDLE hClip = NULL;
367 UINT cfFormat = 0;
368
369 /** @todo r=andy Only one clipboard format can be set at once, at least on Windows. */
370
371 if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
372 {
373 LogFunc(("VBOX_WM_SHCLPB_SET_FORMATS: CF_UNICODETEXT\n"));
374 hClip = SetClipboardData(CF_UNICODETEXT, NULL);
375 }
376 else if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
377 {
378 LogFunc(("VBOX_WM_SHCLPB_SET_FORMATS: CF_DIB\n"));
379 hClip = SetClipboardData(CF_DIB, NULL);
380 }
381 else if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
382 {
383 LogFunc(("VBOX_WM_SHCLPB_SET_FORMATS: VBOX_CLIPBOARD_WIN_REGFMT_HTML\n"));
384 cfFormat = RegisterClipboardFormat(VBOX_CLIPBOARD_WIN_REGFMT_HTML);
385 if (cfFormat != 0)
386 hClip = SetClipboardData(cfFormat, NULL);
387 }
388#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
389 else if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
390 {
391 LogFunc(("VBOX_WM_SHCLPB_SET_FORMATS: VBOX_CLIPBOARD_WIN_REGFMT_URI_LIST cTransfers=%RU32\n",
392 pWinCtx->URI.cTransfers));
393 if (pWinCtx->URI.cTransfers == 0) /* Only allow one transfer at a time for now. */
394 {
395 pWinCtx->URI.Transfer.pDataObj = new VBoxClipboardWinDataObject(pWinCtx->URI.Transfer.pProvider);
396 if (pWinCtx->URI.Transfer.pDataObj)
397 {
398 rc = pWinCtx->URI.Transfer.pDataObj->Init();
399 if (RT_SUCCESS(rc))
400 {
401 VBoxClipboardWinClose();
402 /* Note: Clipboard must be closed first before calling OleSetClipboard(). */
403
404 /** @todo There is a potential race between VBoxClipboardWinClose() and OleSetClipboard(),
405 * where another application could own the clipboard (open), and thus the call to
406 * OleSetClipboard() will fail. Needs fixing. */
407
408 HRESULT hr = OleSetClipboard(pWinCtx->URI.Transfer.pDataObj);
409 if (SUCCEEDED(hr))
410 {
411 pWinCtx->URI.cTransfers++;
412 }
413 else
414 LogRel(("Clipboard: Failed with %Rhrc when setting data object to clipboard\n", hr));
415 }
416 }
417 }
418 else
419 LogRel(("Clipboard: Only one transfer at a time supported (current %RU32 transfer(s) active), skipping\n",
420 pWinCtx->URI.cTransfers));
421 }
422#endif
423 else
424 LogRel(("Clipboard: Unsupported format(s) (0x%x), skipping\n", fFormats));
425
426 /** @todo Implement more flexible clipboard precedence for supported formats. */
427
428 VBoxClipboardWinClose();
429
430 LogFunc(("VBOX_WM_SHCLPB_SET_FORMATS: cfFormat=%u, lastErr=%ld\n", cfFormat, GetLastError()));
431 }
432 }
433 break;
434
435 case VBOX_CLIPBOARD_WM_READ_DATA:
436 {
437 /* Send data in the specified format to the host. */
438 VBOXCLIPBOARDFORMAT uFormat = (uint32_t)lParam;
439 HANDLE hClip = NULL;
440
441 LogFlowFunc(("VBOX_WM_SHCLPB_READ_DATA: uFormat=0x%x\n", uFormat));
442
443 int rc = VBoxClipboardWinOpen(hwnd);
444 if (RT_SUCCESS(rc))
445 {
446 if (uFormat == VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
447 {
448 hClip = GetClipboardData(CF_DIB);
449 if (hClip != NULL)
450 {
451 LPVOID lp = GlobalLock(hClip);
452 if (lp != NULL)
453 {
454 rc = VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_BITMAP,
455 lp, GlobalSize(hClip));
456 GlobalUnlock(hClip);
457 }
458 else
459 {
460 hClip = NULL;
461 }
462 }
463 }
464 else if (uFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
465 {
466 hClip = GetClipboardData(CF_UNICODETEXT);
467 if (hClip != NULL)
468 {
469 LPWSTR uniString = (LPWSTR)GlobalLock(hClip);
470 if (uniString != NULL)
471 {
472 rc = VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
473 uniString, (lstrlenW(uniString) + 1) * 2);
474 GlobalUnlock(hClip);
475 }
476 else
477 {
478 hClip = NULL;
479 }
480 }
481 }
482 else if (uFormat == VBOX_SHARED_CLIPBOARD_FMT_HTML)
483 {
484 UINT format = RegisterClipboardFormat(VBOX_CLIPBOARD_WIN_REGFMT_HTML);
485 if (format != 0)
486 {
487 hClip = GetClipboardData(format);
488 if (hClip != NULL)
489 {
490 LPVOID lp = GlobalLock(hClip);
491
492 if (lp != NULL)
493 {
494 rc = VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_HTML,
495 lp, GlobalSize(hClip));
496 GlobalUnlock(hClip);
497 }
498 else
499 {
500 hClip = NULL;
501 }
502 }
503 }
504 }
505#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
506 else if (uFormat == VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
507 {
508 /* The data data in CF_HDROP format, as the files are locally present and don't need to be
509 * presented as a IDataObject or IStream. */
510 hClip = GetClipboardData(CF_HDROP);
511 if (hClip)
512 {
513 HDROP hDrop = (HDROP)GlobalLock(hClip);
514 if (hDrop)
515 {
516 char *pszList;
517 size_t cbList;
518 rc = VBoxClipboardWinDropFilesToStringList((DROPFILES *)hDrop, &pszList, &cbList);
519 if (RT_SUCCESS(rc))
520 {
521 rc = VbglR3ClipboardWriteData(pCtx->u32ClientID, uFormat, pszList, (uint32_t)cbList);
522 RTMemFree(pszList);
523 }
524
525 GlobalUnlock(hClip);
526 }
527 else
528 {
529 hClip = NULL;
530 }
531 }
532 }
533#endif
534 if (hClip == NULL)
535 {
536 LogFunc(("VBOX_WM_SHCLPB_READ_DATA: hClip=NULL, lastError=%ld\n", GetLastError()));
537
538 /* Requested clipboard format is not available, send empty data. */
539 VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_NONE, NULL, 0);
540 }
541
542 VBoxClipboardWinClose();
543 }
544 }
545 break;
546
547 case WM_DESTROY:
548 {
549 VBoxClipboardWinRemoveFromCBChain(pWinCtx);
550 if (pWinCtx->oldAPI.timerRefresh)
551 KillTimer(pWinCtx->hWnd, 0);
552 /*
553 * don't need to call PostQuitMessage cause
554 * the VBoxTray already finished a message loop
555 */
556 }
557 break;
558
559 default:
560 {
561 lresultRc = DefWindowProc(hwnd, msg, wParam, lParam);
562 }
563 break;
564 }
565
566#ifndef DEBUG_andy
567 LogFlowFunc(("vboxClipboardProcessMsg returned with lresultRc=%ld\n", lresultRc));
568#endif
569 return lresultRc;
570}
571
572static LRESULT CALLBACK vboxClipboardWinWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
573
574static int vboxClipboardCreateWindow(PVBOXCLIPBOARDCONTEXT pCtx)
575{
576 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
577
578 int rc = VINF_SUCCESS;
579
580 AssertPtr(pCtx->pEnv);
581 HINSTANCE hInstance = pCtx->pEnv->hInstance;
582 Assert(hInstance != 0);
583
584 /* Register the Window Class. */
585 WNDCLASSEX wc = { 0 };
586 wc.cbSize = sizeof(WNDCLASSEX);
587
588 if (!GetClassInfoEx(hInstance, s_szClipWndClassName, &wc))
589 {
590 wc.style = CS_NOCLOSE;
591 wc.lpfnWndProc = vboxClipboardWinWndProc;
592 wc.hInstance = pCtx->pEnv->hInstance;
593 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
594 wc.lpszClassName = s_szClipWndClassName;
595
596 ATOM wndClass = RegisterClassEx(&wc);
597 if (wndClass == 0)
598 rc = RTErrConvertFromWin32(GetLastError());
599 }
600
601 if (RT_SUCCESS(rc))
602 {
603 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
604
605 /* Create the window. */
606 pWinCtx->hWnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
607 s_szClipWndClassName, s_szClipWndClassName,
608 WS_POPUPWINDOW,
609 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
610 if (pWinCtx->hWnd == NULL)
611 {
612 rc = VERR_NOT_SUPPORTED;
613 }
614 else
615 {
616 SetWindowPos(pWinCtx->hWnd, HWND_TOPMOST, -200, -200, 0, 0,
617 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
618
619 VBoxClipboardWinAddToCBChain(pWinCtx);
620 if (!VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI))
621 pWinCtx->oldAPI.timerRefresh = SetTimer(pWinCtx->hWnd, 0, 10 * 1000 /* 10s */, NULL);
622 }
623 }
624
625 LogFlowFuncLeaveRC(rc);
626 return rc;
627}
628
629static void vboxClipboardDestroy(PVBOXCLIPBOARDCONTEXT pCtx)
630{
631 AssertPtrReturnVoid(pCtx);
632
633 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
634
635 if (pWinCtx->hWnd)
636 {
637 DestroyWindow(pWinCtx->hWnd);
638 pWinCtx->hWnd = NULL;
639 }
640
641 UnregisterClass(s_szClipWndClassName, pCtx->pEnv->hInstance);
642}
643
644static LRESULT CALLBACK vboxClipboardWinWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
645{
646 PVBOXCLIPBOARDCONTEXT pCtx = &g_Ctx; /** @todo r=andy Make pCtx available through SetWindowLongPtr() / GWL_USERDATA. */
647 AssertPtr(pCtx);
648
649 /* Forward with proper context. */
650 return vboxClipboardWinProcessMsg(pCtx, hWnd, uMsg, wParam, lParam);
651}
652
653DECLCALLBACK(int) VBoxClipboardInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
654{
655 LogFlowFuncEnter();
656
657 PVBOXCLIPBOARDCONTEXT pCtx = &g_Ctx; /* Only one instance for now. */
658 AssertPtr(pCtx);
659
660 if (pCtx->pEnv)
661 {
662 /* Clipboard was already initialized. 2 or more instances are not supported. */
663 return VERR_NOT_SUPPORTED;
664 }
665
666 if (VbglR3AutoLogonIsRemoteSession())
667 {
668 /* Do not use clipboard for remote sessions. */
669 LogRel(("Clipboard: Clipboard has been disabled for a remote session\n"));
670 return VERR_NOT_SUPPORTED;
671 }
672
673 pCtx->pEnv = pEnv;
674
675 int rc = VINF_SUCCESS;
676
677#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
678 HRESULT hr = OleInitialize(NULL);
679 if (FAILED(hr))
680 {
681 LogRel(("Clipboard: Initializing OLE failed (%Rhrc) -- file transfers unavailable\n"));
682 /* Not critical, the rest of the clipboard might work. */
683 }
684 else
685 {
686 LogRel(("Clipboard: Initialized OLE\n"));
687 rc = VBoxClipboardWinURIInit(&pCtx->Win.URI, SharedClipboardProvider::SourceType_VbglR3);
688 }
689#endif
690
691 if (RT_SUCCESS(rc))
692 {
693 /* Check if new Clipboard API is available. */
694 /* ignore rc */ VBoxClipboardWinCheckAndInitNewAPI(&pCtx->Win.newAPI);
695
696 rc = VbglR3ClipboardConnect(&pCtx->u32ClientID);
697 if (RT_SUCCESS(rc))
698 {
699 rc = vboxClipboardCreateWindow(pCtx);
700 if (RT_SUCCESS(rc))
701 {
702 *ppInstance = pCtx;
703 }
704 else
705 {
706 VbglR3ClipboardDisconnect(pCtx->u32ClientID);
707 }
708 }
709 }
710
711 LogFlowFuncLeaveRC(rc);
712 return rc;
713}
714
715DECLCALLBACK(int) VBoxClipboardWorker(void *pInstance, bool volatile *pfShutdown)
716{
717 AssertPtr(pInstance);
718 LogFlowFunc(("pInstance=%p\n", pInstance));
719
720 /*
721 * Tell the control thread that it can continue
722 * spawning services.
723 */
724 RTThreadUserSignal(RTThreadSelf());
725
726 const PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
727 AssertPtr(pCtx);
728
729 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
730
731 int rc;
732
733 /* The thread waits for incoming messages from the host. */
734 for (;;)
735 {
736 uint32_t u32Msg;
737 uint32_t u32Formats;
738 rc = VbglR3ClipboardGetHostMsg(pCtx->u32ClientID, &u32Msg, &u32Formats);
739 if (RT_FAILURE(rc))
740 {
741 if (rc == VERR_INTERRUPTED)
742 break;
743
744 LogFunc(("Error getting host message, rc=%Rrc\n", rc));
745
746 if (*pfShutdown)
747 break;
748
749 /* Wait a bit before retrying. */
750 RTThreadSleep(1000);
751 continue;
752 }
753 else
754 {
755 LogFlowFunc(("u32Msg=%RU32, u32Formats=0x%x\n", u32Msg, u32Formats));
756 switch (u32Msg)
757 {
758 case VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS:
759 {
760 /* The host has announced available clipboard formats.
761 * Forward the information to the window, so it can later
762 * respond to WM_RENDERFORMAT message. */
763 ::PostMessage(pWinCtx->hWnd, VBOX_CLIPBOARD_WM_SET_FORMATS, 0, u32Formats);
764 break;
765 }
766
767 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
768 {
769 /* The host needs data in the specified format. */
770 ::PostMessage(pWinCtx->hWnd, VBOX_CLIPBOARD_WM_READ_DATA, 0, u32Formats);
771 break;
772 }
773
774 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
775 {
776 /* The host is terminating. */
777 LogRel(("Clipboard: Terminating ...\n"));
778 ASMAtomicXchgBool(pfShutdown, true);
779 break;
780 }
781
782 default:
783 {
784 LogFlowFunc(("Unsupported message from host, message=%RU32\n", u32Msg));
785
786 /* Wait a bit before retrying. */
787 RTThreadSleep(1000);
788 break;
789 }
790 }
791 }
792
793 if (*pfShutdown)
794 break;
795 }
796
797 LogFlowFuncLeaveRC(rc);
798 return rc;
799}
800
801DECLCALLBACK(int) VBoxClipboardStop(void *pInstance)
802{
803 AssertPtrReturn(pInstance, VERR_INVALID_POINTER);
804
805 LogFunc(("Stopping pInstance=%p\n", pInstance));
806
807 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
808 AssertPtr(pCtx);
809
810 VbglR3ClipboardDisconnect(pCtx->u32ClientID);
811 pCtx->u32ClientID = 0;
812
813 LogFlowFuncLeaveRC(VINF_SUCCESS);
814 return VINF_SUCCESS;
815}
816
817DECLCALLBACK(void) VBoxClipboardDestroy(void *pInstance)
818{
819 AssertPtrReturnVoid(pInstance);
820
821 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
822 AssertPtr(pCtx);
823
824 /* Make sure that we are disconnected. */
825 Assert(pCtx->u32ClientID == 0);
826
827 vboxClipboardDestroy(pCtx);
828
829#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
830 OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
831 OleUninitialize();
832
833 VBoxClipboardWinURIDestroy(&pCtx->Win.URI);
834#endif
835
836 return;
837}
838
839/**
840 * The service description.
841 */
842VBOXSERVICEDESC g_SvcDescClipboard =
843{
844 /* pszName. */
845 "clipboard",
846 /* pszDescription. */
847 "Shared Clipboard",
848 /* methods */
849 VBoxClipboardInit,
850 VBoxClipboardWorker,
851 VBoxClipboardStop,
852 VBoxClipboardDestroy
853};
854
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