VirtualBox

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

Last change on this file since 105745 was 103631, checked in by vboxsync, 9 months ago

Shared Clipboard: More cleanups (renaming Windows parts to match the other platforms). bugref:9437

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