VirtualBox

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

Last change on this file since 97282 was 97282, checked in by vboxsync, 2 years ago

Shared Clipboard/Service: Factored out and renamed vboxClipboardSvcWinDataSet() into common Shared Clipboard Windows code to SharedClipboardWinDataWrite(), to also make use of such basic functionality in i.e. the unit tests.

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