VirtualBox

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

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

scm copyright and license note update

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