VirtualBox

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

Last change on this file since 103363 was 103363, checked in by vboxsync, 12 months ago

Shared Clipboard: Added a dedicated VERR_SHCLPB_NO_DATA error code, to indicate that clipboard data for a format currently is not available.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.3 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-win.cpp 103363 2024-02-14 17:23:47Z 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 return ShClSvcReadDataFromGuest(pCtx->pClient, uFmt, ppvData, pcbData);
178}
179
180static int vboxClipboardSvcWinReadDataFromGuest(PSHCLCONTEXT pCtx, UINT uWinFormat, void **ppvData, uint32_t *pcbData)
181{
182 SHCLFORMAT uVBoxFmt = SharedClipboardWinClipboardFormatToVBox(uWinFormat);
183 if (uVBoxFmt == VBOX_SHCL_FMT_NONE)
184 {
185 LogRel2(("Shared Clipboard: Windows format %u not supported, ignoring\n", uWinFormat));
186 return VERR_NOT_SUPPORTED;
187 }
188
189 int rc = vboxClipboardSvcWinReadDataFromGuestWorker(pCtx, uVBoxFmt, ppvData, pcbData);
190
191 LogFlowFuncLeaveRC(rc);
192 return rc;
193}
194
195/**
196 * @copydoc SHCLCALLBACKS::pfnOnRequestDataFromSource
197 *
198 * Called from the IDataObject implementation to request data from the guest.
199 *
200 * @thread Windows event thread.
201 */
202static DECLCALLBACK(int) vboxClipboardSvcWinRequestDataFromSourceCallback(PSHCLCONTEXT pCtx,
203 SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
204{
205 RT_NOREF(pvUser);
206
207 LogFlowFuncEnter();
208
209 int rc = vboxClipboardSvcWinReadDataFromGuestWorker(pCtx, uFmt, ppv, pcb);
210
211 LogFlowFuncLeaveRC(rc);
212
213 return rc;
214}
215
216
217#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
218/**
219 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnCreated
220 *
221 * @thread Service main thread.
222 */
223static DECLCALLBACK(void) shClSvcWinTransferOnCreatedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
224{
225 LogFlowFuncEnter();
226
227 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
228 AssertPtr(pCtx);
229
230 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
231 AssertPtr(pTransfer);
232
233 PSHCLCLIENT const pClient = pCtx->pClient;
234 AssertPtr(pClient);
235
236 /*
237 * Set transfer provider.
238 * Those will be registered within ShClSvcTransferInit() when a new transfer gets initialized.
239 */
240
241 /* Set the interface to the local provider by default first. */
242 RT_ZERO(pClient->Transfers.Provider);
243 ShClTransferProviderLocalQueryInterface(&pClient->Transfers.Provider);
244
245 PSHCLTXPROVIDERIFACE pIface = &pClient->Transfers.Provider.Interface;
246
247 pClient->Transfers.Provider.enmSource = pClient->State.enmSource;
248 pClient->Transfers.Provider.pvUser = pClient;
249
250 int rc = VINF_SUCCESS;
251
252 switch (ShClTransferGetDir(pTransfer))
253 {
254 case SHCLTRANSFERDIR_FROM_REMOTE: /* G->H */
255 {
256 pIface->pfnRootListRead = shClSvcTransferIfaceGHRootListRead;
257
258 pIface->pfnListOpen = shClSvcTransferIfaceGHListOpen;
259 pIface->pfnListClose = shClSvcTransferIfaceGHListClose;
260 pIface->pfnListHdrRead = shClSvcTransferIfaceGHListHdrRead;
261 pIface->pfnListEntryRead = shClSvcTransferIfaceGHListEntryRead;
262
263 pIface->pfnObjOpen = shClSvcTransferIfaceGHObjOpen;
264 pIface->pfnObjClose = shClSvcTransferIfaceGHObjClose;
265 pIface->pfnObjRead = shClSvcTransferIfaceGHObjRead;
266 break;
267 }
268
269 case SHCLTRANSFERDIR_TO_REMOTE: /* H->G */
270 {
271 pIface->pfnRootListRead = shClSvcWinTransferIfaceHGRootListRead;
272 break;
273 }
274
275 default:
276 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
277 break;
278 }
279
280 if (RT_SUCCESS(rc))
281 {
282 rc = ShClTransferSetProvider(pTransfer, &pClient->Transfers.Provider);
283 if (RT_SUCCESS(rc))
284 rc = SharedClipboardWinTransferCreate(&pCtx->Win, pTransfer);
285 }
286
287 LogFlowFuncLeaveRC(rc);
288}
289
290/**
291 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnInitialized
292 *
293 * For G->H: Called on transfer intialization to notify the "in-flight" IDataObject about a data transfer.
294 * For H->G: Called on transfer intialization to populate the transfer's root list.
295 *
296 * @thread Service main thread.
297 */
298static DECLCALLBACK(void) shClSvcWinTransferOnInitializedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
299{
300 LogFlowFuncEnter();
301
302 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
303 AssertPtr(pCtx);
304
305 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
306 AssertPtr(pTransfer);
307
308 int rc = VINF_SUCCESS;
309
310 switch (ShClTransferGetDir(pTransfer))
311 {
312 case SHCLTRANSFERDIR_FROM_REMOTE: /* G->H */
313 {
314 rc = RTCritSectEnter(&pCtx->Win.CritSect);
315 if (RT_SUCCESS(rc))
316 {
317 SharedClipboardWinDataObject *pObj = pCtx->Win.pDataObjInFlight;
318 if (pObj)
319 {
320 rc = pObj->SetTransfer(pTransfer);
321 if (RT_SUCCESS(rc))
322 rc = pObj->SetStatus(SharedClipboardWinDataObject::Running);
323
324 pCtx->Win.pDataObjInFlight = NULL; /* Hand off to Windows. */
325 }
326 else
327 AssertMsgFailed(("No data object in flight!\n"));
328
329 int rc2 = RTCritSectLeave(&pCtx->Win.CritSect);
330 AssertRC(rc2);
331 }
332
333 break;
334 }
335
336 case SHCLTRANSFERDIR_TO_REMOTE: /* H->G */
337 {
338 rc = ShClTransferRootListRead(pTransfer); /* Calls shClSvcWinTransferIfaceHGRootListRead(). */
339 break;
340 }
341
342 default:
343 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
344 break;
345 }
346
347 LogFlowFuncLeaveRC(rc);
348}
349
350/**
351 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnDestroy
352 *
353 * @thread Service main thread.
354 */
355static DECLCALLBACK(void) shClSvcWinTransferOnDestroyCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
356{
357 LogFlowFuncEnter();
358
359 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
360 AssertPtr(pCtx);
361
362 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
363 AssertPtr(pTransfer);
364
365 SharedClipboardWinTransferDestroy(&pCtx->Win, pTransfer);
366}
367
368/**
369 * @copydoc SharedClipboardWinDataObject::CALLBACKS::pfnTransferBegin
370 *
371 * Called by SharedClipboardWinDataObject::GetData() when the user wants to paste data.
372 * This then creates and initializes a new transfer on the host + lets the guest know about that new transfer.
373 *
374 * @thread Service main thread.
375 */
376static DECLCALLBACK(int) shClSvcWinDataObjectTransferBeginCallback(SharedClipboardWinDataObject::PCALLBACKCTX pCbCtx)
377{
378 LogFlowFuncEnter();
379
380 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
381 AssertPtr(pCtx);
382
383 PSHCLTRANSFER pTransfer;
384 int rc = ShClSvcTransferCreate(pCtx->pClient, SHCLTRANSFERDIR_FROM_REMOTE, SHCLSOURCE_REMOTE,
385 NIL_SHCLTRANSFERID /* Creates a new transfer ID */, &pTransfer);
386 if (RT_SUCCESS(rc))
387 {
388 /* Initialize the transfer on the host side. */
389 rc = ShClSvcTransferInit(pCtx->pClient, pTransfer);
390 if (RT_FAILURE(rc))
391 ShClSvcTransferDestroy(pCtx->pClient, pTransfer);
392 }
393
394 LogFlowFuncLeaveRC(rc);
395 return rc;
396}
397#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
398
399static LRESULT CALLBACK vboxClipboardSvcWinWndProcMain(PSHCLCONTEXT pCtx,
400 HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
401{
402 AssertPtr(pCtx);
403
404 LRESULT lresultRc = 0;
405
406 const PSHCLWINCTX pWinCtx = &pCtx->Win;
407
408 switch (uMsg)
409 {
410 case WM_CLIPBOARDUPDATE:
411 {
412 LogFunc(("WM_CLIPBOARDUPDATE\n"));
413
414 int rc = RTCritSectEnter(&pWinCtx->CritSect);
415 if (RT_SUCCESS(rc))
416 {
417 const HWND hWndClipboardOwner = GetClipboardOwner();
418
419 LogFunc(("WM_CLIPBOARDUPDATE: hWndClipboardOwnerUs=%p, hWndNewClipboardOwner=%p\n",
420 pWinCtx->hWndClipboardOwnerUs, hWndClipboardOwner));
421
422 if (pWinCtx->hWndClipboardOwnerUs != hWndClipboardOwner)
423 {
424 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
425 AssertRC(rc2);
426
427 /* Clipboard was updated by another application, retrieve formats and report back. */
428 rc = vboxClipboardSvcWinSyncInternal(pCtx);
429 }
430 else
431 {
432 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
433 AssertRC(rc2);
434 }
435 }
436
437 if (RT_FAILURE(rc))
438 LogRel(("Shared Clipboard: WM_CLIPBOARDUPDATE failed with %Rrc\n", rc));
439
440 break;
441 }
442
443 case WM_CHANGECBCHAIN:
444 {
445 LogFunc(("WM_CHANGECBCHAIN\n"));
446 lresultRc = SharedClipboardWinHandleWMChangeCBChain(pWinCtx, hWnd, uMsg, wParam, lParam);
447 break;
448 }
449
450 case WM_DRAWCLIPBOARD:
451 {
452 LogFunc(("WM_DRAWCLIPBOARD\n"));
453
454 int rc = RTCritSectEnter(&pWinCtx->CritSect);
455 if (RT_SUCCESS(rc))
456 {
457 const HWND hWndClipboardOwner = GetClipboardOwner();
458
459 LogFunc(("WM_DRAWCLIPBOARD: hWndClipboardOwnerUs=%p, hWndNewClipboardOwner=%p\n",
460 pWinCtx->hWndClipboardOwnerUs, hWndClipboardOwner));
461
462 if (pWinCtx->hWndClipboardOwnerUs != hWndClipboardOwner)
463 {
464 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
465 AssertRC(rc2);
466
467 /* Clipboard was updated by another application, retrieve formats and report back. */
468 rc = vboxClipboardSvcWinSyncInternal(pCtx);
469 }
470 else
471 {
472 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
473 AssertRC(rc2);
474 }
475 }
476
477 lresultRc = SharedClipboardWinChainPassToNext(pWinCtx, uMsg, wParam, lParam);
478 break;
479 }
480
481 case WM_TIMER:
482 {
483 int rc = SharedClipboardWinHandleWMTimer(pWinCtx);
484 AssertRC(rc);
485
486 break;
487 }
488
489 case WM_RENDERFORMAT:
490 {
491 /* Insert the requested clipboard format data into the clipboard. */
492 const UINT uFmtWin = (UINT)wParam;
493 const SHCLFORMAT uFmtVBox = SharedClipboardWinClipboardFormatToVBox(uFmtWin);
494
495 LogFunc(("WM_RENDERFORMAT: uFmtWin=%u -> uFmtVBox=0x%x\n", uFmtWin, uFmtVBox));
496#ifdef LOG_ENABLED
497 char *pszFmts = ShClFormatsToStrA(uFmtVBox);
498 AssertPtrReturn(pszFmts, 0);
499 LogRel(("Shared Clipboard: Rendering Windows format %#x as VBox format '%s'\n", uFmtWin, pszFmts));
500 RTStrFree(pszFmts);
501#endif
502 if ( uFmtVBox == VBOX_SHCL_FMT_NONE
503 || pCtx->pClient == NULL)
504 {
505 /* Unsupported clipboard format is requested. */
506 LogFunc(("WM_RENDERFORMAT unsupported format requested or client is not active\n"));
507 SharedClipboardWinClear();
508 }
509 else
510 {
511 void *pvData = NULL;
512 uint32_t cbData = 0;
513 int rc = ShClSvcReadDataFromGuest(pCtx->pClient, uFmtVBox, &pvData, &cbData);
514 if (RT_SUCCESS(rc))
515 {
516 /* Wrap HTML clipboard content info CF_HTML format if needed. */
517 if (uFmtVBox == VBOX_SHCL_FMT_HTML
518 && !SharedClipboardWinIsCFHTML((char *)pvData))
519 {
520 char *pszWrapped = NULL;
521 uint32_t cbWrapped = 0;
522 rc = SharedClipboardWinConvertMIMEToCFHTML((char *)pvData, cbData, &pszWrapped, &cbWrapped);
523 if (RT_SUCCESS(rc))
524 {
525 /* Replace buffer with wrapped data content. */
526 RTMemFree(pvData);
527 pvData = (void *)pszWrapped;
528 cbData = cbWrapped;
529 }
530 else
531 LogRel(("Shared Clipboard: cannot convert HTML clipboard into CF_HTML format, rc=%Rrc\n", rc));
532 }
533
534 rc = SharedClipboardWinDataWrite(uFmtWin, pvData, cbData);
535 if (RT_FAILURE(rc))
536 LogRel(("Shared Clipboard: Setting clipboard data for Windows host failed with %Rrc\n", rc));
537
538 RTMemFree(pvData);
539 cbData = 0;
540 }
541
542 if (RT_FAILURE(rc))
543 SharedClipboardWinClear();
544 }
545
546 break;
547 }
548
549 case WM_RENDERALLFORMATS:
550 {
551 LogFunc(("WM_RENDERALLFORMATS\n"));
552
553 int rc = SharedClipboardWinHandleWMRenderAllFormats(pWinCtx, hWnd);
554 AssertRC(rc);
555
556 break;
557 }
558
559 case SHCL_WIN_WM_REPORT_FORMATS: /* Guest reported clipboard formats. */
560 {
561 /* Announce available formats. Do not insert data -- will be inserted in WM_RENDERFORMAT (or via IDataObject). */
562 SHCLFORMATS fFormats = (uint32_t)lParam;
563 LogFunc(("SHCL_WIN_WM_REPORT_FORMATS: fFormats=%#xn", fFormats));
564
565 int rc = SharedClipboardWinClearAndAnnounceFormats(pWinCtx, fFormats, hWnd);
566#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
567 if ( RT_SUCCESS(rc)
568 && fFormats & VBOX_SHCL_FMT_URI_LIST)
569 {
570 /*
571 * Create our IDataObject implementation and push it to the Windows clibpoard.
572 * That way Windows will recognize that there is a data transfer available.
573 */
574 SharedClipboardWinDataObject::CALLBACKS Callbacks;
575 RT_ZERO(Callbacks);
576 Callbacks.pfnTransferBegin = shClSvcWinDataObjectTransferBeginCallback;
577
578 rc = SharedClipboardWinTransferCreateAndSetDataObject(pWinCtx, pCtx, &Callbacks);
579 }
580#else
581 RT_NOREF(rc);
582#endif
583 LogFunc(("SHCL_WIN_WM_REPORT_FORMATS: lastErr=%ld\n", GetLastError()));
584 break;
585 }
586
587 case WM_DESTROY:
588 {
589 LogFunc(("WM_DESTROY\n"));
590
591 int rc = SharedClipboardWinHandleWMDestroy(pWinCtx);
592 AssertRC(rc);
593
594 PostQuitMessage(0);
595 break;
596 }
597
598 default:
599 lresultRc = DefWindowProc(hWnd, uMsg, wParam, lParam);
600 break;
601 }
602
603 LogFlowFunc(("LEAVE hWnd=%p, WM_ %u -> %#zx\n", hWnd, uMsg, lresultRc));
604 return lresultRc;
605}
606
607/**
608 * Static helper function for having a per-client proxy window instances.
609 */
610static LRESULT CALLBACK vboxClipboardSvcWinWndProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
611{
612 LONG_PTR pUserData = GetWindowLongPtr(hWnd, GWLP_USERDATA);
613 AssertPtrReturn(pUserData, 0);
614
615 PSHCLCONTEXT pCtx = reinterpret_cast<PSHCLCONTEXT>(pUserData);
616 if (pCtx)
617 return vboxClipboardSvcWinWndProcMain(pCtx, hWnd, uMsg, wParam, lParam);
618
619 return 0;
620}
621
622/**
623 * Static helper function for routing Windows messages to a specific
624 * proxy window instance.
625 */
626static LRESULT CALLBACK vboxClipboardSvcWinWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
627{
628 /* Note: WM_NCCREATE is not the first ever message which arrives, but
629 * early enough for us. */
630 if (uMsg == WM_NCCREATE)
631 {
632 LogFlowFunc(("WM_NCCREATE\n"));
633
634 LPCREATESTRUCT pCS = (LPCREATESTRUCT)lParam;
635 AssertPtr(pCS);
636 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pCS->lpCreateParams);
637 SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)vboxClipboardSvcWinWndProcInstance);
638
639 return vboxClipboardSvcWinWndProcInstance(hWnd, uMsg, wParam, lParam);
640 }
641
642 /* No window associated yet. */
643 return DefWindowProc(hWnd, uMsg, wParam, lParam);
644}
645
646DECLCALLBACK(int) vboxClipboardSvcWinThread(RTTHREAD hThreadSelf, void *pvUser)
647{
648 LogFlowFuncEnter();
649
650 bool fThreadSignalled = false;
651
652 const PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pvUser;
653 AssertPtr(pCtx);
654 const PSHCLWINCTX pWinCtx = &pCtx->Win;
655
656 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
657
658 /* Register the Window Class. */
659 WNDCLASS wc;
660 RT_ZERO(wc);
661
662 wc.style = CS_NOCLOSE;
663 wc.lpfnWndProc = vboxClipboardSvcWinWndProc;
664 wc.hInstance = hInstance;
665 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
666
667 /* Register an unique wnd class name. */
668 char szWndClassName[32];
669 RTStrPrintf2(szWndClassName, sizeof(szWndClassName),
670 "%s-%RU64", SHCL_WIN_WNDCLASS_NAME, RTThreadGetNative(hThreadSelf));
671 wc.lpszClassName = szWndClassName;
672
673 int rc;
674
675 ATOM atomWindowClass = RegisterClass(&wc);
676 if (atomWindowClass == 0)
677 {
678 LogFunc(("Failed to register window class\n"));
679 rc = VERR_NOT_SUPPORTED;
680 }
681 else
682 {
683 /* Create a window and make it a clipboard viewer. */
684 pWinCtx->hWnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
685 szWndClassName, szWndClassName,
686 WS_POPUPWINDOW,
687 -200, -200, 100, 100, NULL, NULL, hInstance, pCtx /* lpParam */);
688 if (pWinCtx->hWnd == NULL)
689 {
690 LogFunc(("Failed to create window\n"));
691 rc = VERR_NOT_SUPPORTED;
692 }
693 else
694 {
695 SetWindowPos(pWinCtx->hWnd, HWND_TOPMOST, -200, -200, 0, 0,
696 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
697
698 rc = SharedClipboardWinChainAdd(&pCtx->Win);
699 if (RT_SUCCESS(rc))
700 {
701 if (!SharedClipboardWinIsNewAPI(&pWinCtx->newAPI))
702 pWinCtx->oldAPI.timerRefresh = SetTimer(pWinCtx->hWnd, 0, 10 * 1000, NULL);
703 }
704
705#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
706 if (RT_SUCCESS(rc))
707 {
708 HRESULT hr = OleInitialize(NULL);
709 if (FAILED(hr))
710 {
711 LogRel(("Shared Clipboard: Initializing window thread OLE failed (%Rhrc) -- file transfers unavailable\n", hr));
712 /* Not critical, the rest of the clipboard might work. */
713 }
714 else
715 LogRel(("Shared Clipboard: Initialized window thread OLE\n"));
716 }
717#endif
718 int rc2 = RTThreadUserSignal(hThreadSelf);
719 AssertRC(rc2);
720
721 fThreadSignalled = true;
722
723 MSG msg;
724 BOOL msgret = 0;
725 while ((msgret = GetMessage(&msg, NULL, 0, 0)) > 0)
726 {
727 TranslateMessage(&msg);
728 DispatchMessage(&msg);
729 }
730
731 /*
732 * Window procedure can return error, * but this is exceptional situation that should be
733 * identified in testing.
734 */
735 Assert(msgret >= 0);
736 LogFunc(("Message loop finished. GetMessage returned %d, message id: %d \n", msgret, msg.message));
737
738#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
739 OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
740 OleUninitialize();
741#endif
742 }
743 }
744
745 pWinCtx->hWnd = NULL;
746
747 if (atomWindowClass != 0)
748 {
749 UnregisterClass(szWndClassName, hInstance);
750 atomWindowClass = 0;
751 }
752
753 if (!fThreadSignalled)
754 {
755 int rc2 = RTThreadUserSignal(hThreadSelf);
756 AssertRC(rc2);
757 }
758
759 LogFlowFuncLeaveRC(rc);
760 return rc;
761}
762
763/**
764 * Synchronizes the host and the guest clipboard formats by sending all supported host clipboard
765 * formats to the guest.
766 *
767 * @returns VBox status code, VINF_NO_CHANGE if no synchronization was required.
768 * @param pCtx Clipboard context to synchronize.
769 */
770static int vboxClipboardSvcWinSyncInternal(PSHCLCONTEXT pCtx)
771{
772 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
773
774 LogFlowFuncEnter();
775
776 int rc;
777
778 if (pCtx->pClient)
779 {
780 SHCLFORMATS fFormats = 0;
781 rc = SharedClipboardWinGetFormats(&pCtx->Win, &fFormats);
782 if (RT_SUCCESS(rc))
783 rc = ShClSvcReportFormats(pCtx->pClient, fFormats);
784 }
785 else /* If we don't have any client data (yet), bail out. */
786 rc = VINF_NO_CHANGE;
787
788 LogFlowFuncLeaveRC(rc);
789 return rc;
790}
791
792
793/*********************************************************************************************************************************
794* Backend implementation *
795*********************************************************************************************************************************/
796int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable)
797{
798 RT_NOREF(pBackend, pTable);
799#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
800 HRESULT hr = OleInitialize(NULL);
801 if (FAILED(hr))
802 {
803 LogRel(("Shared Clipboard: Initializing OLE failed (%Rhrc) -- file transfers unavailable\n", hr));
804 /* Not critical, the rest of the clipboard might work. */
805 }
806 else
807 LogRel(("Shared Clipboard: Initialized OLE\n"));
808#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
809
810 return VINF_SUCCESS;
811}
812
813void ShClBackendDestroy(PSHCLBACKEND pBackend)
814{
815 RT_NOREF(pBackend);
816
817#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
818 OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
819 OleUninitialize();
820#endif
821}
822
823int ShClBackendConnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, bool fHeadless)
824{
825 RT_NOREF(pBackend, fHeadless);
826
827 LogFlowFuncEnter();
828
829 int rc;
830
831 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)RTMemAllocZ(sizeof(SHCLCONTEXT));
832 if (pCtx)
833 {
834 rc = SharedClipboardWinCtxInit(&pCtx->Win);
835 if (RT_SUCCESS(rc))
836 {
837 rc = RTThreadCreate(&pCtx->hThread, vboxClipboardSvcWinThread, pCtx /* pvUser */, _64K /* Stack size */,
838 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "ShClWin");
839 if (RT_SUCCESS(rc))
840 {
841 int rc2 = RTThreadUserWait(pCtx->hThread, RT_MS_30SEC /* Timeout in ms */);
842 AssertRC(rc2);
843 }
844 }
845
846 pClient->State.pCtx = pCtx;
847 pClient->State.pCtx->pClient = pClient;
848
849#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
850 /*
851 * Set callbacks.
852 * Those will be registered within ShClSvcTransferInit() when a new transfer gets initialized.
853 */
854 RT_ZERO(pClient->Transfers.Callbacks);
855
856 pClient->Transfers.Callbacks.pvUser = pCtx; /* Assign context as user-provided callback data. */
857 pClient->Transfers.Callbacks.cbUser = sizeof(SHCLCONTEXT);
858
859 pClient->Transfers.Callbacks.pfnOnCreated = shClSvcWinTransferOnCreatedCallback;
860 pClient->Transfers.Callbacks.pfnOnInitialized = shClSvcWinTransferOnInitializedCallback;
861 pClient->Transfers.Callbacks.pfnOnDestroy = shClSvcWinTransferOnDestroyCallback;
862#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
863 }
864 else
865 rc = VERR_NO_MEMORY;
866
867 LogFlowFuncLeaveRC(rc);
868 return rc;
869}
870
871int ShClBackendSync(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
872{
873 RT_NOREF(pBackend);
874
875 /* Sync the host clipboard content with the client. */
876 return vboxClipboardSvcWinSyncInternal(pClient->State.pCtx);
877}
878
879int ShClBackendDisconnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
880{
881 RT_NOREF(pBackend);
882
883 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
884
885 LogFlowFuncEnter();
886
887 int rc = VINF_SUCCESS;
888
889 PSHCLCONTEXT pCtx = pClient->State.pCtx;
890 if (pCtx)
891 {
892 if (pCtx->Win.hWnd)
893 PostMessage(pCtx->Win.hWnd, WM_DESTROY, 0 /* wParam */, 0 /* lParam */);
894
895 if (pCtx->hThread != NIL_RTTHREAD)
896 {
897 LogFunc(("Waiting for thread to terminate ...\n"));
898
899 /* Wait for the window thread to terminate. */
900 rc = RTThreadWait(pCtx->hThread, RT_MS_30SEC /* Timeout in ms */, NULL);
901 if (RT_FAILURE(rc))
902 LogRel(("Shared Clipboard: Waiting for window thread termination failed with rc=%Rrc\n", rc));
903
904 pCtx->hThread = NIL_RTTHREAD;
905 }
906
907 SharedClipboardWinCtxDestroy(&pCtx->Win);
908
909 if (RT_SUCCESS(rc))
910 {
911 RTMemFree(pCtx);
912 pCtx = NULL;
913
914 pClient->State.pCtx = NULL;
915 }
916 }
917
918 LogFlowFuncLeaveRC(rc);
919 return rc;
920}
921
922int ShClBackendReportFormats(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, SHCLFORMATS fFormats)
923{
924 RT_NOREF(pBackend);
925
926 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
927
928 PSHCLCONTEXT pCtx = pClient->State.pCtx;
929 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
930
931 LogFlowFunc(("fFormats=0x%x, hWnd=%p\n", fFormats, pCtx->Win.hWnd));
932
933 /*
934 * The guest announced formats. Forward to the window thread.
935 */
936 PostMessage(pCtx->Win.hWnd, SHCL_WIN_WM_REPORT_FORMATS, 0 /* wParam */, fFormats /* lParam */);
937
938 LogFlowFuncLeaveRC(VINF_SUCCESS);
939 return VINF_SUCCESS;
940}
941
942int ShClBackendReadData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
943 SHCLFORMAT uFmt, void *pvData, uint32_t cbData, uint32_t *pcbActual)
944{
945 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
946 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
947 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
948 AssertPtrReturn(pcbActual, VERR_INVALID_POINTER);
949
950 RT_NOREF(pBackend, pCmdCtx);
951
952 AssertPtrReturn(pClient->State.pCtx, VERR_INVALID_POINTER);
953
954 LogFlowFunc(("uFmt=%#x\n", uFmt));
955
956 HANDLE hClip = NULL;
957
958 const PSHCLWINCTX pWinCtx = &pClient->State.pCtx->Win;
959
960 /*
961 * The guest wants to read data in the given format.
962 */
963 int rc = SharedClipboardWinOpen(pWinCtx->hWnd);
964 if (RT_SUCCESS(rc))
965 {
966 if (uFmt & VBOX_SHCL_FMT_BITMAP)
967 {
968 LogFunc(("CF_DIB\n"));
969 hClip = GetClipboardData(CF_DIB);
970 if (hClip != NULL)
971 {
972 LPVOID lp = GlobalLock(hClip);
973 if (lp != NULL)
974 {
975 rc = vboxClipboardSvcWinDataGet(VBOX_SHCL_FMT_BITMAP, lp, GlobalSize(hClip),
976 pvData, cbData, pcbActual);
977 GlobalUnlock(hClip);
978 }
979 else
980 {
981 hClip = NULL;
982 }
983 }
984 }
985 else if (uFmt & VBOX_SHCL_FMT_UNICODETEXT)
986 {
987 LogFunc(("CF_UNICODETEXT\n"));
988 hClip = GetClipboardData(CF_UNICODETEXT);
989 if (hClip != NULL)
990 {
991 LPWSTR uniString = (LPWSTR)GlobalLock(hClip);
992 if (uniString != NULL)
993 {
994 rc = vboxClipboardSvcWinDataGet(VBOX_SHCL_FMT_UNICODETEXT, uniString, (lstrlenW(uniString) + 1) * 2,
995 pvData, cbData, pcbActual);
996 GlobalUnlock(hClip);
997 }
998 else
999 {
1000 hClip = NULL;
1001 }
1002 }
1003 }
1004 else if (uFmt & VBOX_SHCL_FMT_HTML)
1005 {
1006 LogFunc(("SHCL_WIN_REGFMT_HTML\n"));
1007 UINT uRegFmt = RegisterClipboardFormat(SHCL_WIN_REGFMT_HTML);
1008 if (uRegFmt != 0)
1009 {
1010 hClip = GetClipboardData(uRegFmt);
1011 if (hClip != NULL)
1012 {
1013 LPVOID lp = GlobalLock(hClip);
1014 if (lp != NULL)
1015 {
1016 rc = vboxClipboardSvcWinDataGet(VBOX_SHCL_FMT_HTML, lp, GlobalSize(hClip),
1017 pvData, cbData, pcbActual);
1018#ifdef LOG_ENABLED
1019 if (RT_SUCCESS(rc))
1020 {
1021 LogFlowFunc(("Raw HTML clipboard data from host:\n"));
1022 ShClDbgDumpHtml((char *)pvData, cbData);
1023 }
1024#endif
1025 GlobalUnlock(hClip);
1026 }
1027 else
1028 {
1029 hClip = NULL;
1030 }
1031 }
1032 }
1033 }
1034#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1035 else if (uFmt & VBOX_SHCL_FMT_URI_LIST)
1036 {
1037 hClip = hClip = GetClipboardData(CF_HDROP);
1038 if (hClip)
1039 {
1040 HDROP hDrop = (HDROP)GlobalLock(hClip);
1041 if (hDrop)
1042 {
1043 char *pszList = NULL;
1044 uint32_t cbList;
1045 rc = SharedClipboardWinTransferDropFilesToStringList((DROPFILES *)hDrop, &pszList, &cbList);
1046
1047 GlobalUnlock(hClip);
1048
1049 if (RT_SUCCESS(rc))
1050 {
1051 if (cbList <= cbData)
1052 {
1053 memcpy(pvData, pszList, cbList);
1054 *pcbActual = cbList;
1055 }
1056
1057 RTStrFree(pszList);
1058 }
1059 }
1060 else
1061 LogRel(("Shared Clipboard: Unable to lock clipboard data, last error: %ld\n", GetLastError()));
1062 }
1063 else
1064 LogRel(("Shared Clipboard: Unable to retrieve clipboard data from clipboard (CF_HDROP), last error: %ld\n",
1065 GetLastError()));
1066 }
1067#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
1068 SharedClipboardWinClose();
1069 }
1070
1071 if (RT_FAILURE(rc))
1072 LogRel(("Shared Clipboard: Error reading host clipboard data in format %#x from Windows, rc=%Rrc\n", uFmt, rc));
1073
1074 LogFlowFuncLeaveRC(rc);
1075 return rc;
1076}
1077
1078int ShClBackendWriteData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
1079 SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
1080{
1081 RT_NOREF(pBackend, pClient, pCmdCtx, uFormat, pvData, cbData);
1082
1083 LogFlowFuncEnter();
1084
1085 /* Nothing to do here yet. */
1086
1087 LogFlowFuncLeave();
1088 return VINF_SUCCESS;
1089}
1090
1091#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1092/**
1093 * Handles transfer status replies from the guest.
1094 */
1095int ShClBackendTransferHandleStatusReply(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLSOURCE enmSource, SHCLTRANSFERSTATUS enmStatus, int rcStatus)
1096{
1097 RT_NOREF(pBackend, pClient, pTransfer, enmSource, enmStatus, rcStatus);
1098
1099 return VINF_SUCCESS;
1100}
1101
1102
1103/*********************************************************************************************************************************
1104* Provider interface implementation *
1105*********************************************************************************************************************************/
1106
1107/** @copydoc SHCLTXPROVIDERIFACE::pfnRootListRead */
1108static DECLCALLBACK(int) shClSvcWinTransferIfaceHGRootListRead(PSHCLTXPROVIDERCTX pCtx)
1109{
1110 LogFlowFuncEnter();
1111
1112 PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser;
1113 AssertPtr(pClient);
1114
1115 AssertPtr(pClient->State.pCtx);
1116 PSHCLWINCTX pWin = &pClient->State.pCtx->Win;
1117
1118 int rc = SharedClipboardWinTransferGetRootsFromClipboard(pWin, pCtx->pTransfer);
1119
1120 LogFlowFuncLeaveRC(rc);
1121 return rc;
1122}
1123#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
1124
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