VirtualBox

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

最後變更 在這個檔案從100370是 100370,由 vboxsync 提交於 21 月 前

Shared Clipboard: Removed ShClBackendTransferHGRootListRead(), not needed. ​​bugref:9437

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

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