VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp@ 100378

Last change on this file since 100378 was 100378, checked in by vboxsync, 20 months ago

Shared Clipboard: Installed Windows host callbacks for reading the host root list. ​​bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.7 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-win.cpp 100378 2023-07-05 09:04:11Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Win32 host.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
33#include <iprt/win/windows.h>
34
35#include <VBox/HostServices/VBoxClipboardSvc.h>
36#include <VBox/GuestHost/clipboard-helper.h>
37#include <VBox/GuestHost/SharedClipboard-win.h>
38#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
39# include <VBox/GuestHost/SharedClipboard-transfers.h>
40#endif
41
42#include <iprt/alloc.h>
43#include <iprt/string.h>
44#include <iprt/asm.h>
45#include <iprt/assert.h>
46#include <iprt/ldr.h>
47#include <iprt/semaphore.h>
48#include <iprt/thread.h>
49#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
50# include <iprt/utf16.h>
51#endif
52
53#include <process.h>
54#include <iprt/win/shlobj.h> /* Needed for shell objects. */
55
56#include "VBoxSharedClipboardSvc-internal.h"
57#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
58# include "VBoxSharedClipboardSvc-transfers.h"
59#endif
60
61
62/*********************************************************************************************************************************
63* Structures and Typedefs *
64*********************************************************************************************************************************/
65/**
66 * Global context information used by the host glue for the X11 clipboard backend.
67 */
68struct SHCLCONTEXT
69{
70 /** Handle for window message handling thread. */
71 RTTHREAD hThread;
72 /** Structure for keeping and communicating with service client. */
73 PSHCLCLIENT pClient;
74 /** Windows-specific context data. */
75 SHCLWINCTX Win;
76};
77
78
79/*********************************************************************************************************************************
80* Prototypes *
81*********************************************************************************************************************************/
82static int vboxClipboardSvcWinSyncInternal(PSHCLCONTEXT pCtx);
83
84#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
85static DECLCALLBACK(int) shClSvcWinTransferIfaceHGRootListRead(PSHCLTXPROVIDERCTX pCtx);
86#endif
87
88
89/**
90 * Copy clipboard data into the guest buffer.
91 *
92 * At first attempt, guest will provide a buffer of default size.
93 * Usually 1K or 4K (see platform specific Guest Additions code around
94 * VbglR3ClipboardReadData calls). If this buffer is not big enough
95 * to fit host clipboard content, this function will return VINF_BUFFER_OVERFLOW
96 * and provide guest with host's clipboard buffer actual size. This will be a
97 * signal for the guest to re-read host clipboard data providing bigger buffer
98 * to store it.
99 *
100 * @returns IPRT status code.
101 * @returns VINF_BUFFER_OVERFLOW returned when guest buffer size if not big
102 * enough to store host clipboard data. This is a signal to the guest
103 * to re-issue host clipboard read request with bigger buffer size
104 * (specified in @a pcbActualDst output parameter).
105 * @param u32Format VBox clipboard format (VBOX_SHCL_FMT_XXX) of copied data.
106 * VBOX_SHCL_FMT_NONE returns 0 data.
107 * @param pvSrc Pointer to host clipboard data.
108 * @param cbSrc Size (in bytes) of actual clipboard data to copy.
109 * @param pvDst Pointer to guest buffer to store clipboard data.
110 * @param cbDst Size (in bytes) of guest buffer.
111 * @param pcbActualDst Actual size (in bytes) of host clipboard data.
112 * Only set if guest buffer size if not big enough
113 * to store host clipboard content. When set,
114 * function returns VINF_BUFFER_OVERFLOW.
115 */
116static int vboxClipboardSvcWinDataGet(SHCLFORMAT u32Format, const void *pvSrc, uint32_t cbSrc,
117 void *pvDst, uint32_t cbDst, uint32_t *pcbActualDst)
118{
119 AssertPtrReturn(pcbActualDst, VERR_INVALID_POINTER);
120 if (u32Format == VBOX_SHCL_FMT_NONE)
121 {
122 *pcbActualDst = 0;
123 return VINF_SUCCESS;
124 }
125
126 AssertPtrReturn(pvSrc, VERR_INVALID_POINTER);
127 AssertReturn (cbSrc, VERR_INVALID_PARAMETER);
128 AssertPtrReturn(pvDst, VERR_INVALID_POINTER);
129 AssertReturn (cbDst, VERR_INVALID_PARAMETER);
130
131 LogFlowFunc(("cbSrc = %d, cbDst = %d\n", cbSrc, cbDst));
132
133 if ( u32Format == VBOX_SHCL_FMT_HTML
134 && SharedClipboardWinIsCFHTML((const char *)pvSrc))
135 {
136 /** @todo r=bird: Why the double conversion? */
137 char *pszBuf = NULL;
138 uint32_t cbBuf = 0;
139 int rc = SharedClipboardWinConvertCFHTMLToMIME((const char *)pvSrc, cbSrc, &pszBuf, &cbBuf);
140 if (RT_SUCCESS(rc))
141 {
142 *pcbActualDst = cbBuf;
143 if (cbBuf > cbDst)
144 {
145 /* Do not copy data. The dst buffer is not enough. */
146 RTMemFree(pszBuf);
147 return VINF_BUFFER_OVERFLOW;
148 }
149 memcpy(pvDst, pszBuf, cbBuf);
150 RTMemFree(pszBuf);
151 }
152 else
153 *pcbActualDst = 0;
154 }
155 else
156 {
157 *pcbActualDst = cbSrc; /* Tell the caller how much space we need. */
158
159 if (cbSrc > cbDst)
160 return VINF_BUFFER_OVERFLOW;
161
162 memcpy(pvDst, pvSrc, cbSrc);
163 }
164
165#ifdef LOG_ENABLED
166 ShClDbgDumpData(pvDst, cbSrc, u32Format);
167#endif
168
169 return VINF_SUCCESS;
170}
171
172/**
173 * Worker for a reading clipboard from the guest.
174 */
175static int vboxClipboardSvcWinReadDataFromGuestWorker(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppvData, uint32_t *pcbData)
176{
177 LogFlowFunc(("uFmt=%#x\n", uFmt));
178
179 int rc = ShClSvcReadDataFromGuest(pCtx->pClient, uFmt, ppvData, pcbData);
180 if (RT_FAILURE(rc))
181 LogRel(("Shared Clipboard: Reading guest clipboard data for Windows host failed with %Rrc\n", rc));
182
183 LogFlowFuncLeaveRC(rc);
184 return rc;
185}
186
187static int vboxClipboardSvcWinReadDataFromGuest(PSHCLCONTEXT pCtx, UINT uWinFormat, void **ppvData, uint32_t *pcbData)
188{
189 SHCLFORMAT uVBoxFmt = SharedClipboardWinClipboardFormatToVBox(uWinFormat);
190 if (uVBoxFmt == VBOX_SHCL_FMT_NONE)
191 {
192 LogRel2(("Shared Clipboard: Windows format %u not supported, ignoring\n", uWinFormat));
193 return VERR_NOT_SUPPORTED;
194 }
195
196 int rc = vboxClipboardSvcWinReadDataFromGuestWorker(pCtx, uVBoxFmt, ppvData, pcbData);
197
198 LogFlowFuncLeaveRC(rc);
199 return rc;
200}
201
202/**
203 * @copydoc SHCLCALLBACKS::pfnOnRequestDataFromSource
204 *
205 * Called from the IDataObject implementation to request data from the guest.
206 *
207 * @thread Windows event thread.
208 */
209static DECLCALLBACK(int) vboxClipboardSvcWinRequestDataFromSourceCallback(PSHCLCONTEXT pCtx,
210 SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
211{
212 RT_NOREF(pvUser);
213
214 LogFlowFuncEnter();
215
216 int rc = vboxClipboardSvcWinReadDataFromGuestWorker(pCtx, uFmt, ppv, pcb);
217
218 LogFlowFuncLeaveRC(rc);
219
220 return rc;
221}
222
223
224#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
225/**
226 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnCreated
227 *
228 * @thread Service main thread.
229 */
230static DECLCALLBACK(void) shClSvcWinTransferOnCreatedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
231{
232 LogFlowFuncEnter();
233
234 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
235 AssertPtr(pCtx);
236
237 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
238 AssertPtr(pTransfer);
239
240 PSHCLCLIENT const pClient = pCtx->pClient;
241 AssertPtr(pClient);
242
243 /*
244 * Set transfer provider.
245 * Those will be registered within ShClSvcTransferInit() when a new transfer gets initialized.
246 */
247
248 /* Set the interface to the local provider by default first. */
249 RT_ZERO(pClient->Transfers.Provider);
250 ShClTransferProviderLocalQueryInterface(&pClient->Transfers.Provider);
251
252 PSHCLTXPROVIDERIFACE pIface = &pClient->Transfers.Provider.Interface;
253
254 switch (ShClTransferGetDir(pTransfer))
255 {
256 case SHCLTRANSFERDIR_FROM_REMOTE: /* Guest -> Host. */
257 {
258 pIface->pfnRootListRead = shClSvcTransferIfaceGHRootListRead;
259
260 pIface->pfnListOpen = shClSvcTransferIfaceGHListOpen;
261 pIface->pfnListClose = shClSvcTransferIfaceGHListClose;
262 pIface->pfnListHdrRead = shClSvcTransferIfaceGHListHdrRead;
263 pIface->pfnListEntryRead = shClSvcTransferIfaceGHListEntryRead;
264
265 pIface->pfnObjOpen = shClSvcTransferIfaceGHObjOpen;
266 pIface->pfnObjClose = shClSvcTransferIfaceGHObjClose;
267 pIface->pfnObjRead = shClSvcTransferIfaceGHObjRead;
268 break;
269 }
270
271 case SHCLTRANSFERDIR_TO_REMOTE: /* Host -> Guest. */
272 {
273 pIface->pfnRootListRead = shClSvcWinTransferIfaceHGRootListRead;
274 break;
275 }
276
277 default:
278 AssertFailed();
279 }
280
281 int rc = ShClTransferSetProvider(pTransfer, &pClient->Transfers.Provider);
282
283 LogFlowFuncLeaveRC(rc);
284}
285
286/**
287 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnInitialized
288 *
289 * Called on transfer intialization to notify the "in-flight" IDataObject about a data transfer.
290 *
291 * @thread Service main thread.
292 */
293static DECLCALLBACK(void) shClSvcWinTransferOnInitializedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
294{
295 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
296 AssertPtr(pCtx);
297
298 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
299 AssertPtr(pTransfer);
300
301 const SHCLTRANSFERDIR enmDir = ShClTransferGetDir(pTransfer);
302
303 int rc = VINF_SUCCESS;
304
305 LogFlowFunc(("pCtx=%p, idTransfer=%RU32, enmDir=%RU32\n", pCtx, ShClTransferGetID(pTransfer), enmDir));
306
307 /* The host wants to transfer data from the guest. */
308 if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE)
309 {
310 rc = RTCritSectEnter(&pCtx->Win.CritSect);
311 if (RT_SUCCESS(rc))
312 {
313 SharedClipboardWinDataObject *pObj = pCtx->Win.pDataObjInFlight;
314 AssertPtrReturnVoid(pObj);
315 rc = pObj->SetTransfer(pTransfer);
316
317 pCtx->Win.pDataObjInFlight = NULL; /* Hand off to Windows. */
318
319 int rc2 = RTCritSectLeave(&pCtx->Win.CritSect);
320 AssertRC(rc2);
321 }
322 }
323
324 if (RT_FAILURE(rc))
325 LogRel(("Shared Clipboard: Starting transfer failed, rc=%Rrc\n", rc));
326
327 LogFlowFunc(("LEAVE: idTransfer=%RU32, rc=%Rrc\n", ShClTransferGetID(pTransfer), rc));
328}
329
330/**
331 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnDestroy
332 *
333 * @thread Service main thread.
334 */
335static DECLCALLBACK(void) shClSvcWinTransferOnDestroyCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
336{
337 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
338 AssertPtr(pCtx);
339
340 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
341 AssertPtr(pTransfer);
342
343 SharedClipboardWinTransferDestroy(&pCtx->Win, pTransfer);
344}
345
346/**
347 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnStarted
348 *
349 * @thread Service main thread.
350 */
351static DECLCALLBACK(void) shClSvcWinTransferOnStartedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
352{
353 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
354 AssertPtr(pCtx);
355
356 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
357 AssertPtr(pTransfer);
358
359 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_FROM_REMOTE) /* G->H */
360 {
361 /* Report to the guest that we now entered the STARTED state. */
362 ShClSvcTransferStart(pCtx->pClient, pTransfer);
363 }
364}
365#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
366
367static LRESULT CALLBACK vboxClipboardSvcWinWndProcMain(PSHCLCONTEXT pCtx,
368 HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
369{
370 AssertPtr(pCtx);
371
372 LRESULT lresultRc = 0;
373
374 const PSHCLWINCTX pWinCtx = &pCtx->Win;
375
376 switch (uMsg)
377 {
378 case WM_CLIPBOARDUPDATE:
379 {
380 LogFunc(("WM_CLIPBOARDUPDATE\n"));
381
382 int rc = RTCritSectEnter(&pWinCtx->CritSect);
383 if (RT_SUCCESS(rc))
384 {
385 const HWND hWndClipboardOwner = GetClipboardOwner();
386
387 LogFunc(("WM_CLIPBOARDUPDATE: hWndClipboardOwnerUs=%p, hWndNewClipboardOwner=%p\n",
388 pWinCtx->hWndClipboardOwnerUs, hWndClipboardOwner));
389
390 if (pWinCtx->hWndClipboardOwnerUs != hWndClipboardOwner)
391 {
392 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
393 AssertRC(rc2);
394
395 /* Clipboard was updated by another application, retrieve formats and report back. */
396 rc = vboxClipboardSvcWinSyncInternal(pCtx);
397 }
398 else
399 {
400 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
401 AssertRC(rc2);
402 }
403 }
404
405 if (RT_FAILURE(rc))
406 LogRel(("Shared Clipboard: WM_CLIPBOARDUPDATE failed with %Rrc\n", rc));
407
408 break;
409 }
410
411 case WM_CHANGECBCHAIN:
412 {
413 LogFunc(("WM_CHANGECBCHAIN\n"));
414 lresultRc = SharedClipboardWinHandleWMChangeCBChain(pWinCtx, hWnd, uMsg, wParam, lParam);
415 break;
416 }
417
418 case WM_DRAWCLIPBOARD:
419 {
420 LogFunc(("WM_DRAWCLIPBOARD\n"));
421
422 int rc = RTCritSectEnter(&pWinCtx->CritSect);
423 if (RT_SUCCESS(rc))
424 {
425 const HWND hWndClipboardOwner = GetClipboardOwner();
426
427 LogFunc(("WM_DRAWCLIPBOARD: hWndClipboardOwnerUs=%p, hWndNewClipboardOwner=%p\n",
428 pWinCtx->hWndClipboardOwnerUs, hWndClipboardOwner));
429
430 if (pWinCtx->hWndClipboardOwnerUs != hWndClipboardOwner)
431 {
432 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
433 AssertRC(rc2);
434
435 /* Clipboard was updated by another application, retrieve formats and report back. */
436 rc = vboxClipboardSvcWinSyncInternal(pCtx);
437 }
438 else
439 {
440 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
441 AssertRC(rc2);
442 }
443 }
444
445 lresultRc = SharedClipboardWinChainPassToNext(pWinCtx, uMsg, wParam, lParam);
446 break;
447 }
448
449 case WM_TIMER:
450 {
451 int rc = SharedClipboardWinHandleWMTimer(pWinCtx);
452 AssertRC(rc);
453
454 break;
455 }
456
457 case WM_RENDERFORMAT:
458 {
459 LogFunc(("WM_RENDERFORMAT\n"));
460
461 /* Insert the requested clipboard format data into the clipboard. */
462 const UINT uFormat = (UINT)wParam;
463 const SHCLFORMAT fFormat = SharedClipboardWinClipboardFormatToVBox(uFormat);
464 LogFunc(("WM_RENDERFORMAT: uFormat=%u -> fFormat=0x%x\n", uFormat, fFormat));
465
466 if ( fFormat == VBOX_SHCL_FMT_NONE
467 || pCtx->pClient == NULL)
468 {
469 /* Unsupported clipboard format is requested. */
470 LogFunc(("WM_RENDERFORMAT unsupported format requested or client is not active\n"));
471 SharedClipboardWinClear();
472 }
473 else
474 {
475 void *pvData = NULL;
476 uint32_t cbData = 0;
477 int rc = ShClSvcReadDataFromGuest(pCtx->pClient, uFormat, &pvData, &cbData);
478 if ( RT_SUCCESS(rc)
479 && pvData
480 && cbData)
481 {
482 /* Wrap HTML clipboard content info CF_HTML format if needed. */
483 if (fFormat == VBOX_SHCL_FMT_HTML
484 && !SharedClipboardWinIsCFHTML((char *)pvData))
485 {
486 char *pszWrapped = NULL;
487 uint32_t cbWrapped = 0;
488 rc = SharedClipboardWinConvertMIMEToCFHTML((char *)pvData, cbData, &pszWrapped, &cbWrapped);
489 if (RT_SUCCESS(rc))
490 {
491 /* Replace buffer with wrapped data content. */
492 RTMemFree(pvData);
493 pvData = (void *)pszWrapped;
494 cbData = cbWrapped;
495 }
496 else
497 LogRel(("Shared Clipboard: cannot convert HTML clipboard into CF_HTML format, rc=%Rrc\n", rc));
498 }
499
500 rc = SharedClipboardWinDataWrite(uFormat, pvData, cbData);
501 if (RT_FAILURE(rc))
502 LogRel(("Shared Clipboard: Setting clipboard data for Windows host failed with %Rrc\n", rc));
503
504 RTMemFree(pvData);
505 cbData = 0;
506 }
507
508 if (RT_FAILURE(rc))
509 SharedClipboardWinClear();
510 }
511
512 break;
513 }
514
515 case WM_RENDERALLFORMATS:
516 {
517 LogFunc(("WM_RENDERALLFORMATS\n"));
518
519 int rc = SharedClipboardWinHandleWMRenderAllFormats(pWinCtx, hWnd);
520 AssertRC(rc);
521
522 break;
523 }
524
525 case SHCL_WIN_WM_REPORT_FORMATS: /* Guest reported clipboard formats. */
526 {
527 /* Announce available formats. Do not insert data -- will be inserted in WM_RENDERFORMAT (or via IDataObject). */
528 SHCLFORMATS fFormats = (uint32_t)lParam;
529 LogFunc(("SHCL_WIN_WM_REPORT_FORMATS: fFormats=%#xn", fFormats));
530
531 int rc = SharedClipboardWinClearAndAnnounceFormats(pWinCtx, fFormats, hWnd);
532 if (RT_FAILURE(rc))
533 LogRel(("Shared Clipboard: Reporting clipboard formats %#x to Windows host failed with %Rrc\n", fFormats, rc));
534
535 LogFunc(("SHCL_WIN_WM_REPORT_FORMATS: lastErr=%ld\n", GetLastError()));
536 break;
537 }
538
539 case WM_DESTROY:
540 {
541 LogFunc(("WM_DESTROY\n"));
542
543 int rc = SharedClipboardWinHandleWMDestroy(pWinCtx);
544 AssertRC(rc);
545
546 PostQuitMessage(0);
547 break;
548 }
549
550 default:
551 lresultRc = DefWindowProc(hWnd, uMsg, wParam, lParam);
552 break;
553 }
554
555 LogFlowFunc(("LEAVE hWnd=%p, WM_ %u -> %#zx\n", hWnd, uMsg, lresultRc));
556 return lresultRc;
557}
558
559/**
560 * Static helper function for having a per-client proxy window instances.
561 */
562static LRESULT CALLBACK vboxClipboardSvcWinWndProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
563{
564 LONG_PTR pUserData = GetWindowLongPtr(hWnd, GWLP_USERDATA);
565 AssertPtrReturn(pUserData, 0);
566
567 PSHCLCONTEXT pCtx = reinterpret_cast<PSHCLCONTEXT>(pUserData);
568 if (pCtx)
569 return vboxClipboardSvcWinWndProcMain(pCtx, hWnd, uMsg, wParam, lParam);
570
571 return 0;
572}
573
574/**
575 * Static helper function for routing Windows messages to a specific
576 * proxy window instance.
577 */
578static LRESULT CALLBACK vboxClipboardSvcWinWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
579{
580 /* Note: WM_NCCREATE is not the first ever message which arrives, but
581 * early enough for us. */
582 if (uMsg == WM_NCCREATE)
583 {
584 LogFlowFunc(("WM_NCCREATE\n"));
585
586 LPCREATESTRUCT pCS = (LPCREATESTRUCT)lParam;
587 AssertPtr(pCS);
588 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pCS->lpCreateParams);
589 SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)vboxClipboardSvcWinWndProcInstance);
590
591 return vboxClipboardSvcWinWndProcInstance(hWnd, uMsg, wParam, lParam);
592 }
593
594 /* No window associated yet. */
595 return DefWindowProc(hWnd, uMsg, wParam, lParam);
596}
597
598DECLCALLBACK(int) vboxClipboardSvcWinThread(RTTHREAD hThreadSelf, void *pvUser)
599{
600 LogFlowFuncEnter();
601
602 bool fThreadSignalled = false;
603
604 const PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pvUser;
605 AssertPtr(pCtx);
606 const PSHCLWINCTX pWinCtx = &pCtx->Win;
607
608 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
609
610 /* Register the Window Class. */
611 WNDCLASS wc;
612 RT_ZERO(wc);
613
614 wc.style = CS_NOCLOSE;
615 wc.lpfnWndProc = vboxClipboardSvcWinWndProc;
616 wc.hInstance = hInstance;
617 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
618
619 /* Register an unique wnd class name. */
620 char szWndClassName[32];
621 RTStrPrintf2(szWndClassName, sizeof(szWndClassName),
622 "%s-%RU64", SHCL_WIN_WNDCLASS_NAME, RTThreadGetNative(hThreadSelf));
623 wc.lpszClassName = szWndClassName;
624
625 int rc;
626
627 ATOM atomWindowClass = RegisterClass(&wc);
628 if (atomWindowClass == 0)
629 {
630 LogFunc(("Failed to register window class\n"));
631 rc = VERR_NOT_SUPPORTED;
632 }
633 else
634 {
635 /* Create a window and make it a clipboard viewer. */
636 pWinCtx->hWnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
637 szWndClassName, szWndClassName,
638 WS_POPUPWINDOW,
639 -200, -200, 100, 100, NULL, NULL, hInstance, pCtx /* lpParam */);
640 if (pWinCtx->hWnd == NULL)
641 {
642 LogFunc(("Failed to create window\n"));
643 rc = VERR_NOT_SUPPORTED;
644 }
645 else
646 {
647 SetWindowPos(pWinCtx->hWnd, HWND_TOPMOST, -200, -200, 0, 0,
648 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
649
650 rc = SharedClipboardWinChainAdd(&pCtx->Win);
651 if (RT_SUCCESS(rc))
652 {
653 if (!SharedClipboardWinIsNewAPI(&pWinCtx->newAPI))
654 pWinCtx->oldAPI.timerRefresh = SetTimer(pWinCtx->hWnd, 0, 10 * 1000, NULL);
655 }
656
657#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
658 if (RT_SUCCESS(rc))
659 {
660 HRESULT hr = OleInitialize(NULL);
661 if (FAILED(hr))
662 {
663 LogRel(("Shared Clipboard: Initializing window thread OLE failed (%Rhrc) -- file transfers unavailable\n", hr));
664 /* Not critical, the rest of the clipboard might work. */
665 }
666 else
667 LogRel(("Shared Clipboard: Initialized window thread OLE\n"));
668 }
669#endif
670 int rc2 = RTThreadUserSignal(hThreadSelf);
671 AssertRC(rc2);
672
673 fThreadSignalled = true;
674
675 MSG msg;
676 BOOL msgret = 0;
677 while ((msgret = GetMessage(&msg, NULL, 0, 0)) > 0)
678 {
679 TranslateMessage(&msg);
680 DispatchMessage(&msg);
681 }
682
683 /*
684 * Window procedure can return error, * but this is exceptional situation that should be
685 * identified in testing.
686 */
687 Assert(msgret >= 0);
688 LogFunc(("Message loop finished. GetMessage returned %d, message id: %d \n", msgret, msg.message));
689
690#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
691 OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
692 OleUninitialize();
693#endif
694 }
695 }
696
697 pWinCtx->hWnd = NULL;
698
699 if (atomWindowClass != 0)
700 {
701 UnregisterClass(szWndClassName, hInstance);
702 atomWindowClass = 0;
703 }
704
705 if (!fThreadSignalled)
706 {
707 int rc2 = RTThreadUserSignal(hThreadSelf);
708 AssertRC(rc2);
709 }
710
711 LogFlowFuncLeaveRC(rc);
712 return rc;
713}
714
715/**
716 * Synchronizes the host and the guest clipboard formats by sending all supported host clipboard
717 * formats to the guest.
718 *
719 * @returns VBox status code, VINF_NO_CHANGE if no synchronization was required.
720 * @param pCtx Clipboard context to synchronize.
721 */
722static int vboxClipboardSvcWinSyncInternal(PSHCLCONTEXT pCtx)
723{
724 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
725
726 LogFlowFuncEnter();
727
728 int rc;
729
730 if (pCtx->pClient)
731 {
732 SHCLFORMATS fFormats = 0;
733 rc = SharedClipboardWinGetFormats(&pCtx->Win, &fFormats);
734 if ( RT_SUCCESS(rc)
735 && fFormats != VBOX_SHCL_FMT_NONE /** @todo r=bird: BUGBUG: revisit this. */
736 && ShClSvcIsBackendActive())
737 rc = ShClSvcHostReportFormats(pCtx->pClient, fFormats);
738 }
739 else /* If we don't have any client data (yet), bail out. */
740 rc = VINF_NO_CHANGE;
741
742 LogFlowFuncLeaveRC(rc);
743 return rc;
744}
745
746
747/*********************************************************************************************************************************
748* Backend implementation *
749*********************************************************************************************************************************/
750int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable)
751{
752 RT_NOREF(pBackend, pTable);
753#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
754 HRESULT hr = OleInitialize(NULL);
755 if (FAILED(hr))
756 {
757 LogRel(("Shared Clipboard: Initializing OLE failed (%Rhrc) -- file transfers unavailable\n", hr));
758 /* Not critical, the rest of the clipboard might work. */
759 }
760 else
761 LogRel(("Shared Clipboard: Initialized OLE\n"));
762#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
763
764 return VINF_SUCCESS;
765}
766
767void ShClBackendDestroy(PSHCLBACKEND pBackend)
768{
769 RT_NOREF(pBackend);
770
771#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
772 OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
773 OleUninitialize();
774#endif
775}
776
777int ShClBackendConnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, bool fHeadless)
778{
779 RT_NOREF(pBackend, fHeadless);
780
781 LogFlowFuncEnter();
782
783 int rc;
784
785 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)RTMemAllocZ(sizeof(SHCLCONTEXT));
786 if (pCtx)
787 {
788 rc = SharedClipboardWinCtxInit(&pCtx->Win);
789 if (RT_SUCCESS(rc))
790 {
791 rc = RTThreadCreate(&pCtx->hThread, vboxClipboardSvcWinThread, pCtx /* pvUser */, _64K /* Stack size */,
792 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "ShClWin");
793 if (RT_SUCCESS(rc))
794 {
795 int rc2 = RTThreadUserWait(pCtx->hThread, RT_MS_30SEC /* Timeout in ms */);
796 AssertRC(rc2);
797 }
798 }
799
800 pClient->State.pCtx = pCtx;
801 pClient->State.pCtx->pClient = pClient;
802
803#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
804 /*
805 * Init transfer callbacks.
806 */
807 RT_ZERO(pClient->Transfers.Callbacks);
808 pClient->Transfers.Callbacks.pfnOnInitialized = shClSvcWinTransferOnInitializedCallback;
809 pClient->Transfers.Callbacks.pfnOnStarted = shClSvcWinTransferOnStartedCallback;
810 pClient->Transfers.Callbacks.pfnOnDestroy = shClSvcWinTransferOnDestroyCallback;
811
812 pClient->Transfers.Callbacks.pvUser = pCtx; /* Assign context as user-provided callback data. */
813 pClient->Transfers.Callbacks.cbUser = sizeof(SHCLCONTEXT);
814#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
815 }
816 else
817 rc = VERR_NO_MEMORY;
818
819 LogFlowFuncLeaveRC(rc);
820 return rc;
821}
822
823int ShClBackendSync(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
824{
825 RT_NOREF(pBackend);
826
827 /* Sync the host clipboard content with the client. */
828 return vboxClipboardSvcWinSyncInternal(pClient->State.pCtx);
829}
830
831int ShClBackendDisconnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
832{
833 RT_NOREF(pBackend);
834
835 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
836
837 LogFlowFuncEnter();
838
839 int rc = VINF_SUCCESS;
840
841 PSHCLCONTEXT pCtx = pClient->State.pCtx;
842 if (pCtx)
843 {
844 if (pCtx->Win.hWnd)
845 PostMessage(pCtx->Win.hWnd, WM_DESTROY, 0 /* wParam */, 0 /* lParam */);
846
847 if (pCtx->hThread != NIL_RTTHREAD)
848 {
849 LogFunc(("Waiting for thread to terminate ...\n"));
850
851 /* Wait for the window thread to terminate. */
852 rc = RTThreadWait(pCtx->hThread, RT_MS_30SEC /* Timeout in ms */, NULL);
853 if (RT_FAILURE(rc))
854 LogRel(("Shared Clipboard: Waiting for window thread termination failed with rc=%Rrc\n", rc));
855
856 pCtx->hThread = NIL_RTTHREAD;
857 }
858
859 SharedClipboardWinCtxDestroy(&pCtx->Win);
860
861 if (RT_SUCCESS(rc))
862 {
863 RTMemFree(pCtx);
864 pCtx = NULL;
865
866 pClient->State.pCtx = NULL;
867 }
868 }
869
870 LogFlowFuncLeaveRC(rc);
871 return rc;
872}
873
874int ShClBackendReportFormats(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, SHCLFORMATS fFormats)
875{
876 RT_NOREF(pBackend);
877
878 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
879
880 PSHCLCONTEXT pCtx = pClient->State.pCtx;
881 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
882
883 LogFlowFunc(("fFormats=0x%x, hWnd=%p\n", fFormats, pCtx->Win.hWnd));
884
885 /*
886 * The guest announced formats. Forward to the window thread.
887 */
888 PostMessage(pCtx->Win.hWnd, SHCL_WIN_WM_REPORT_FORMATS, 0 /* wParam */, fFormats /* lParam */);
889
890 LogFlowFuncLeaveRC(VINF_SUCCESS);
891 return VINF_SUCCESS;
892}
893
894int ShClBackendReadData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
895 SHCLFORMAT uFmt, void *pvData, uint32_t cbData, uint32_t *pcbActual)
896{
897 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
898 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
899 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
900 AssertPtrReturn(pcbActual, VERR_INVALID_POINTER);
901
902 RT_NOREF(pBackend, pCmdCtx);
903
904 AssertPtrReturn(pClient->State.pCtx, VERR_INVALID_POINTER);
905
906 LogFlowFunc(("uFmt=%#x\n", uFmt));
907
908 HANDLE hClip = NULL;
909
910 const PSHCLWINCTX pWinCtx = &pClient->State.pCtx->Win;
911
912 /*
913 * The guest wants to read data in the given format.
914 */
915 int rc = SharedClipboardWinOpen(pWinCtx->hWnd);
916 if (RT_SUCCESS(rc))
917 {
918 if (uFmt & VBOX_SHCL_FMT_BITMAP)
919 {
920 LogFunc(("CF_DIB\n"));
921 hClip = GetClipboardData(CF_DIB);
922 if (hClip != NULL)
923 {
924 LPVOID lp = GlobalLock(hClip);
925 if (lp != NULL)
926 {
927 rc = vboxClipboardSvcWinDataGet(VBOX_SHCL_FMT_BITMAP, lp, GlobalSize(hClip),
928 pvData, cbData, pcbActual);
929 GlobalUnlock(hClip);
930 }
931 else
932 {
933 hClip = NULL;
934 }
935 }
936 }
937 else if (uFmt & VBOX_SHCL_FMT_UNICODETEXT)
938 {
939 LogFunc(("CF_UNICODETEXT\n"));
940 hClip = GetClipboardData(CF_UNICODETEXT);
941 if (hClip != NULL)
942 {
943 LPWSTR uniString = (LPWSTR)GlobalLock(hClip);
944 if (uniString != NULL)
945 {
946 rc = vboxClipboardSvcWinDataGet(VBOX_SHCL_FMT_UNICODETEXT, uniString, (lstrlenW(uniString) + 1) * 2,
947 pvData, cbData, pcbActual);
948 GlobalUnlock(hClip);
949 }
950 else
951 {
952 hClip = NULL;
953 }
954 }
955 }
956 else if (uFmt & VBOX_SHCL_FMT_HTML)
957 {
958 LogFunc(("SHCL_WIN_REGFMT_HTML\n"));
959 UINT uRegFmt = RegisterClipboardFormat(SHCL_WIN_REGFMT_HTML);
960 if (uRegFmt != 0)
961 {
962 hClip = GetClipboardData(uRegFmt);
963 if (hClip != NULL)
964 {
965 LPVOID lp = GlobalLock(hClip);
966 if (lp != NULL)
967 {
968 rc = vboxClipboardSvcWinDataGet(VBOX_SHCL_FMT_HTML, lp, GlobalSize(hClip),
969 pvData, cbData, pcbActual);
970#ifdef LOG_ENABLED
971 if (RT_SUCCESS(rc))
972 {
973 LogFlowFunc(("Raw HTML clipboard data from host:\n"));
974 ShClDbgDumpHtml((char *)pvData, cbData);
975 }
976#endif
977 GlobalUnlock(hClip);
978 }
979 else
980 {
981 hClip = NULL;
982 }
983 }
984 }
985 }
986#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
987 else if (uFmt & VBOX_SHCL_FMT_URI_LIST)
988 {
989 hClip = hClip = GetClipboardData(CF_HDROP);
990 if (hClip)
991 {
992 HDROP hDrop = (HDROP)GlobalLock(hClip);
993 if (hDrop)
994 {
995 char *pszList = NULL;
996 uint32_t cbList;
997 rc = SharedClipboardWinTransferDropFilesToStringList((DROPFILES *)hDrop, &pszList, &cbList);
998
999 GlobalUnlock(hClip);
1000
1001 if (RT_SUCCESS(rc))
1002 {
1003 if (cbList <= cbData)
1004 {
1005 memcpy(pvData, pszList, cbList);
1006 *pcbActual = cbList;
1007 }
1008
1009 RTStrFree(pszList);
1010 }
1011 }
1012 else
1013 LogRel(("Shared Clipboard: Unable to lock clipboard data, last error: %ld\n", GetLastError()));
1014 }
1015 else
1016 LogRel(("Shared Clipboard: Unable to retrieve clipboard data from clipboard (CF_HDROP), last error: %ld\n",
1017 GetLastError()));
1018 }
1019#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
1020 SharedClipboardWinClose();
1021 }
1022
1023 if (RT_FAILURE(rc))
1024 LogRel(("Shared Clipboard: Error reading host clipboard data in format %#x from Windows, rc=%Rrc\n", uFmt, rc));
1025
1026 LogFlowFuncLeaveRC(rc);
1027 return rc;
1028}
1029
1030int ShClBackendWriteData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
1031 SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
1032{
1033 RT_NOREF(pBackend, pClient, pCmdCtx, uFormat, pvData, cbData);
1034
1035 LogFlowFuncEnter();
1036
1037 /* Nothing to do here yet. */
1038
1039 LogFlowFuncLeave();
1040 return VINF_SUCCESS;
1041}
1042
1043#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1044/**
1045 * Handles transfer status replies from the guest.
1046 */
1047int ShClBackendTransferHandleStatusReply(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLSOURCE enmSource, SHCLTRANSFERSTATUS enmStatus, int rcStatus)
1048{
1049 RT_NOREF(pBackend, pClient, pTransfer, enmSource, enmStatus, rcStatus);
1050
1051 return VINF_SUCCESS;
1052}
1053
1054
1055/*********************************************************************************************************************************
1056* Provider interface implementation *
1057*********************************************************************************************************************************/
1058
1059/** @copydoc SHCLTXPROVIDERIFACE::pfnRootListRead */
1060static DECLCALLBACK(int) shClSvcWinTransferIfaceHGRootListRead(PSHCLTXPROVIDERCTX pCtx)
1061{
1062 LogFlowFuncEnter();
1063
1064 PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser;
1065 AssertPtr(pClient);
1066
1067 AssertPtr(pClient->State.pCtx);
1068 PSHCLWINCTX pWin = &pClient->State.pCtx->Win;
1069
1070 int rc = SharedClipboardWinTransferGetRootsFromClipboard(pWin, pCtx->pTransfer);
1071
1072 LogFlowFuncLeaveRC(rc);
1073 return rc;
1074}
1075#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
1076
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