VirtualBox

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

Last change on this file since 92783 was 92739, checked in by vboxsync, 3 years ago

Shared Clipboard/Transfers: Resolved @todos: Revamped event [source] interface to now use the event objects directly instead of the event ID [build fix]. bugref:9437

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