VirtualBox

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

Last change on this file since 100347 was 100205, checked in by vboxsync, 21 months ago

Shared Clipboard: Unified root list entry code to also use the generic list entry code, a lot of updates for the cross OS transfer handling code, more updates for HTTP server transfer handling. This also changed the handling of how that transfers are being initiated, as we needed to have this for X11: Before, transfers were initiated as soon as on side announced the URI list format -- now we postpone initiating the transfer until the receiving side requests the data as URI list [build fix]. ​bugref:9437

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette