VirtualBox

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

Last change on this file since 78725 was 78725, 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.5 KB
Line 
1/* $Id: VBoxClipboard.cpp 78725 2019-05-24 13:15:59Z 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.Provider);
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 }
419#endif
420 else
421 LogRel(("Clipboard: Unsupported format(s) (0x%x), skipping\n", fFormats));
422
423 /** @todo Implement more flexible clipboard precedence for supported formats. */
424
425 VBoxClipboardWinClose();
426 /* Note: Clipboard must be closed first before calling OleSetClipboard(). */
427
428 LogFunc(("VBOX_WM_SHCLPB_SET_FORMATS: cfFormat=%u, lastErr=%ld\n", cfFormat, GetLastError()));
429 }
430 }
431 break;
432
433 case VBOX_CLIPBOARD_WM_READ_DATA:
434 {
435 /* Send data in the specified format to the host. */
436 VBOXCLIPBOARDFORMAT uFormat = (uint32_t)lParam;
437 HANDLE hClip = NULL;
438
439 LogFlowFunc(("VBOX_WM_SHCLPB_READ_DATA: uFormat=0x%x\n", uFormat));
440
441 int rc = VBoxClipboardWinOpen(hwnd);
442 if (RT_SUCCESS(rc))
443 {
444 if (uFormat == VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
445 {
446 hClip = GetClipboardData(CF_DIB);
447 if (hClip != NULL)
448 {
449 LPVOID lp = GlobalLock(hClip);
450 if (lp != NULL)
451 {
452 rc = VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_BITMAP,
453 lp, GlobalSize(hClip));
454 GlobalUnlock(hClip);
455 }
456 else
457 {
458 hClip = NULL;
459 }
460 }
461 }
462 else if (uFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
463 {
464 hClip = GetClipboardData(CF_UNICODETEXT);
465 if (hClip != NULL)
466 {
467 LPWSTR uniString = (LPWSTR)GlobalLock(hClip);
468 if (uniString != NULL)
469 {
470 rc = VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
471 uniString, (lstrlenW(uniString) + 1) * 2);
472 GlobalUnlock(hClip);
473 }
474 else
475 {
476 hClip = NULL;
477 }
478 }
479 }
480 else if (uFormat == VBOX_SHARED_CLIPBOARD_FMT_HTML)
481 {
482 UINT format = RegisterClipboardFormat(VBOX_CLIPBOARD_WIN_REGFMT_HTML);
483 if (format != 0)
484 {
485 hClip = GetClipboardData(format);
486 if (hClip != NULL)
487 {
488 LPVOID lp = GlobalLock(hClip);
489
490 if (lp != NULL)
491 {
492 rc = VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_HTML,
493 lp, GlobalSize(hClip));
494 GlobalUnlock(hClip);
495 }
496 else
497 {
498 hClip = NULL;
499 }
500 }
501 }
502 }
503#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
504 else if (uFormat == VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
505 {
506 /* The data data in CF_HDROP format, as the files are locally present and don't need to be
507 * presented as a IDataObject or IStream. */
508 hClip = GetClipboardData(CF_HDROP);
509 if (hClip)
510 {
511 HDROP hDrop = (HDROP)GlobalLock(hClip);
512 if (hDrop)
513 {
514 char *pszList;
515 size_t cbList;
516 rc = VBoxClipboardWinDropFilesToStringList((DROPFILES *)hDrop, &pszList, &cbList);
517 if (RT_SUCCESS(rc))
518 {
519 rc = VbglR3ClipboardWriteData(pCtx->u32ClientID, uFormat, pszList, (uint32_t)cbList);
520 RTMemFree(pszList);
521 }
522
523 GlobalUnlock(hClip);
524 }
525 else
526 {
527 hClip = NULL;
528 }
529 }
530 }
531#endif
532 if (hClip == NULL)
533 {
534 LogFunc(("VBOX_WM_SHCLPB_READ_DATA: hClip=NULL, lastError=%ld\n", GetLastError()));
535
536 /* Requested clipboard format is not available, send empty data. */
537 VbglR3ClipboardWriteData(pCtx->u32ClientID, VBOX_SHARED_CLIPBOARD_FMT_NONE, NULL, 0);
538 }
539
540 VBoxClipboardWinClose();
541 }
542 }
543 break;
544
545 case WM_DESTROY:
546 {
547 VBoxClipboardWinRemoveFromCBChain(pWinCtx);
548 if (pWinCtx->oldAPI.timerRefresh)
549 KillTimer(pWinCtx->hWnd, 0);
550 /*
551 * don't need to call PostQuitMessage cause
552 * the VBoxTray already finished a message loop
553 */
554 }
555 break;
556
557 default:
558 {
559 lresultRc = DefWindowProc(hwnd, msg, wParam, lParam);
560 }
561 break;
562 }
563
564#ifndef DEBUG_andy
565 LogFlowFunc(("vboxClipboardProcessMsg returned with lresultRc=%ld\n", lresultRc));
566#endif
567 return lresultRc;
568}
569
570static LRESULT CALLBACK vboxClipboardWinWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
571
572static int vboxClipboardCreateWindow(PVBOXCLIPBOARDCONTEXT pCtx)
573{
574 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
575
576 int rc = VINF_SUCCESS;
577
578 AssertPtr(pCtx->pEnv);
579 HINSTANCE hInstance = pCtx->pEnv->hInstance;
580 Assert(hInstance != 0);
581
582 /* Register the Window Class. */
583 WNDCLASSEX wc = { 0 };
584 wc.cbSize = sizeof(WNDCLASSEX);
585
586 if (!GetClassInfoEx(hInstance, s_szClipWndClassName, &wc))
587 {
588 wc.style = CS_NOCLOSE;
589 wc.lpfnWndProc = vboxClipboardWinWndProc;
590 wc.hInstance = pCtx->pEnv->hInstance;
591 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
592 wc.lpszClassName = s_szClipWndClassName;
593
594 ATOM wndClass = RegisterClassEx(&wc);
595 if (wndClass == 0)
596 rc = RTErrConvertFromWin32(GetLastError());
597 }
598
599 if (RT_SUCCESS(rc))
600 {
601 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
602
603 /* Create the window. */
604 pWinCtx->hWnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
605 s_szClipWndClassName, s_szClipWndClassName,
606 WS_POPUPWINDOW,
607 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
608 if (pWinCtx->hWnd == NULL)
609 {
610 rc = VERR_NOT_SUPPORTED;
611 }
612 else
613 {
614 SetWindowPos(pWinCtx->hWnd, HWND_TOPMOST, -200, -200, 0, 0,
615 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
616
617 VBoxClipboardWinAddToCBChain(pWinCtx);
618 if (!VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI))
619 pWinCtx->oldAPI.timerRefresh = SetTimer(pWinCtx->hWnd, 0, 10 * 1000 /* 10s */, NULL);
620 }
621 }
622
623 LogFlowFuncLeaveRC(rc);
624 return rc;
625}
626
627static void vboxClipboardDestroy(PVBOXCLIPBOARDCONTEXT pCtx)
628{
629 AssertPtrReturnVoid(pCtx);
630
631 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
632
633 if (pWinCtx->hWnd)
634 {
635 DestroyWindow(pWinCtx->hWnd);
636 pWinCtx->hWnd = NULL;
637 }
638
639 UnregisterClass(s_szClipWndClassName, pCtx->pEnv->hInstance);
640}
641
642static LRESULT CALLBACK vboxClipboardWinWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
643{
644 PVBOXCLIPBOARDCONTEXT pCtx = &g_Ctx; /** @todo r=andy Make pCtx available through SetWindowLongPtr() / GWL_USERDATA. */
645 AssertPtr(pCtx);
646
647 /* Forward with proper context. */
648 return vboxClipboardWinProcessMsg(pCtx, hWnd, uMsg, wParam, lParam);
649}
650
651DECLCALLBACK(int) VBoxClipboardInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
652{
653 LogFlowFuncEnter();
654
655 PVBOXCLIPBOARDCONTEXT pCtx = &g_Ctx; /* Only one instance for now. */
656 AssertPtr(pCtx);
657
658 if (pCtx->pEnv)
659 {
660 /* Clipboard was already initialized. 2 or more instances are not supported. */
661 return VERR_NOT_SUPPORTED;
662 }
663
664 pCtx->pEnv = pEnv;
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 int rc;
674
675#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
676 HRESULT hr = OleInitialize(NULL);
677 if (FAILED(hr))
678 {
679 LogRel(("Clipboard: Initializing OLE failed (%Rhrc) -- file transfers unavailable\n"));
680 /* Not critical, the rest of the clipboard might work. */
681 }
682 else
683 LogRel(("Clipboard: Initialized OLE\n"));
684
685 rc = pCtx->Win.URI.Transfer.Provider.SetSource(SharedClipboardProvider::SourceType_VbglR3);
686 AssertRC(rc);
687#endif
688
689 /* Check that new Clipboard API is available */
690 VBoxClipboardWinCheckAndInitNewAPI(&pCtx->Win.newAPI);
691
692 rc = VbglR3ClipboardConnect(&pCtx->u32ClientID);
693 if (RT_SUCCESS(rc))
694 {
695 rc = vboxClipboardCreateWindow(pCtx);
696 if (RT_SUCCESS(rc))
697 {
698 *ppInstance = pCtx;
699 }
700 else
701 {
702 VbglR3ClipboardDisconnect(pCtx->u32ClientID);
703 }
704 }
705
706 LogFlowFuncLeaveRC(rc);
707 return rc;
708}
709
710DECLCALLBACK(int) VBoxClipboardWorker(void *pInstance, bool volatile *pfShutdown)
711{
712 AssertPtr(pInstance);
713 LogFlowFunc(("pInstance=%p\n", pInstance));
714
715 /*
716 * Tell the control thread that it can continue
717 * spawning services.
718 */
719 RTThreadUserSignal(RTThreadSelf());
720
721 const PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
722 AssertPtr(pCtx);
723
724 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
725
726 int rc;
727
728 /* The thread waits for incoming messages from the host. */
729 for (;;)
730 {
731 uint32_t u32Msg;
732 uint32_t u32Formats;
733 rc = VbglR3ClipboardGetHostMsg(pCtx->u32ClientID, &u32Msg, &u32Formats);
734 if (RT_FAILURE(rc))
735 {
736 if (rc == VERR_INTERRUPTED)
737 break;
738
739 LogFunc(("Error getting host message, rc=%Rrc\n", rc));
740
741 if (*pfShutdown)
742 break;
743
744 /* Wait a bit before retrying. */
745 RTThreadSleep(1000);
746 continue;
747 }
748 else
749 {
750 LogFlowFunc(("u32Msg=%RU32, u32Formats=0x%x\n", u32Msg, u32Formats));
751 switch (u32Msg)
752 {
753 case VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS:
754 {
755 /* The host has announced available clipboard formats.
756 * Forward the information to the window, so it can later
757 * respond to WM_RENDERFORMAT message. */
758 ::PostMessage(pWinCtx->hWnd, VBOX_CLIPBOARD_WM_SET_FORMATS, 0, u32Formats);
759 break;
760 }
761
762 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
763 {
764 /* The host needs data in the specified format. */
765 ::PostMessage(pWinCtx->hWnd, VBOX_CLIPBOARD_WM_READ_DATA, 0, u32Formats);
766 break;
767 }
768
769 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
770 {
771 /* The host is terminating. */
772 LogRel(("Clipboard: Terminating ...\n"));
773 ASMAtomicXchgBool(pfShutdown, true);
774 break;
775 }
776
777 default:
778 {
779 LogFlowFunc(("Unsupported message from host, message=%RU32\n", u32Msg));
780
781 /* Wait a bit before retrying. */
782 RTThreadSleep(1000);
783 break;
784 }
785 }
786 }
787
788 if (*pfShutdown)
789 break;
790 }
791
792 LogFlowFuncLeaveRC(rc);
793 return rc;
794}
795
796DECLCALLBACK(int) VBoxClipboardStop(void *pInstance)
797{
798 AssertPtrReturn(pInstance, VERR_INVALID_POINTER);
799
800 LogFunc(("Stopping pInstance=%p\n", pInstance));
801
802 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
803 AssertPtr(pCtx);
804
805 VbglR3ClipboardDisconnect(pCtx->u32ClientID);
806 pCtx->u32ClientID = 0;
807
808 LogFlowFuncLeaveRC(VINF_SUCCESS);
809 return VINF_SUCCESS;
810}
811
812DECLCALLBACK(void) VBoxClipboardDestroy(void *pInstance)
813{
814 AssertPtrReturnVoid(pInstance);
815
816 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
817 AssertPtr(pCtx);
818
819 /* Make sure that we are disconnected. */
820 Assert(pCtx->u32ClientID == 0);
821
822 vboxClipboardDestroy(pCtx);
823 RT_BZERO(pCtx, sizeof(VBOXCLIPBOARDCONTEXT));
824
825#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST_ASF
826 OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
827 OleUninitialize();
828#endif
829
830 return;
831}
832
833/**
834 * The service description.
835 */
836VBOXSERVICEDESC g_SvcDescClipboard =
837{
838 /* pszName. */
839 "clipboard",
840 /* pszDescription. */
841 "Shared Clipboard",
842 /* methods */
843 VBoxClipboardInit,
844 VBoxClipboardWorker,
845 VBoxClipboardStop,
846 VBoxClipboardDestroy
847};
848
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