VirtualBox

source: vbox/trunk/src/VBox/GuestHost/SharedClipboard/clipboard-win.cpp@ 78406

Last change on this file since 78406 was 78393, checked in by vboxsync, 6 years ago

Shared Clipboard/URI: Fixed VBoxClipboardWinClose() / VBoxClipboardWinClear().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.2 KB
Line 
1/* $Id: clipboard-win.cpp 78393 2019-05-06 15:02:08Z vboxsync $ */
2/** @file
3 * Shared Clipboard: Windows-specific functions for clipboard handling.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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#include <iprt/alloc.h>
19#include <iprt/assert.h>
20#include <iprt/errcore.h>
21#include <iprt/ldr.h>
22#include <iprt/thread.h>
23
24#include <VBox/log.h>
25#include <VBox/GuestHost/SharedClipboard.h>
26#include <VBox/GuestHost/SharedClipboard-win.h>
27#include <VBox/GuestHost/clipboard-helper.h>
28
29/**
30 * Opens the clipboard of a specific window.
31 *
32 * @returns VBox status code.
33 * @param hWnd Handle of window to open clipboard for.
34 */
35int VBoxClipboardWinOpen(HWND hWnd)
36{
37 /* "OpenClipboard fails if another window has the clipboard open."
38 * So try a few times and wait up to 1 second.
39 */
40 BOOL fOpened = FALSE;
41
42 LogFlowFunc(("hWnd=%p\n", hWnd));
43
44 int i = 0;
45 for (;;)
46 {
47 if (OpenClipboard(hWnd))
48 {
49 fOpened = TRUE;
50 break;
51 }
52
53 if (i >= 10) /* sleep interval = [1..512] ms */
54 break;
55
56 RTThreadSleep(1 << i);
57 ++i;
58 }
59
60#ifdef LOG_ENABLED
61 if (i > 0)
62 LogFlowFunc(("%d times tried to open clipboard\n", i + 1));
63#endif
64
65 int rc;
66 if (fOpened)
67 rc = VINF_SUCCESS;
68 else
69 {
70 const DWORD dwLastErr = GetLastError();
71 rc = RTErrConvertFromWin32(dwLastErr);
72 LogFunc(("Failed to open clipboard, rc=%Rrc (0x%x)\n", rc, dwLastErr));
73 }
74
75 return rc;
76}
77
78/**
79 * Closes the clipboard for the current thread.
80 *
81 * @returns VBox status code.
82 */
83int VBoxClipboardWinClose(void)
84{
85 int rc;
86
87 const BOOL fRc = CloseClipboard();
88 if (RT_UNLIKELY(!fRc))
89 {
90 const DWORD dwLastErr = GetLastError();
91 rc = RTErrConvertFromWin32(dwLastErr);
92 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
93 }
94 else
95 rc = VINF_SUCCESS;
96
97 return rc;
98}
99
100/**
101 * Clears the clipboard for the current thread.
102 *
103 * @returns VBox status code.
104 */
105int VBoxClipboardWinClear(void)
106{
107 int rc;
108
109 const BOOL fRc = EmptyClipboard();
110 if (RT_UNLIKELY(!fRc))
111 {
112 const DWORD dwLastErr = GetLastError();
113 rc = RTErrConvertFromWin32(dwLastErr);
114 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
115 }
116 else
117 rc = VINF_SUCCESS;
118
119 return rc;
120}
121
122/**
123 * Checks and initializes function pointer which are required for using
124 * the new clipboard API.
125 *
126 * @returns VBox status code.
127 * @param pAPI Where to store the retrieved function pointers.
128 * Will be set to NULL if the new API is not available.
129 */
130int VBoxClipboardWinCheckAndInitNewAPI(PVBOXCLIPBOARDWINAPINEW pAPI)
131{
132 RTLDRMOD hUser32 = NIL_RTLDRMOD;
133 int rc = RTLdrLoadSystem("User32.dll", /* fNoUnload = */ true, &hUser32);
134 if (RT_SUCCESS(rc))
135 {
136 rc = RTLdrGetSymbol(hUser32, "AddClipboardFormatListener", (void **)&pAPI->pfnAddClipboardFormatListener);
137 if (RT_SUCCESS(rc))
138 {
139 rc = RTLdrGetSymbol(hUser32, "RemoveClipboardFormatListener", (void **)&pAPI->pfnRemoveClipboardFormatListener);
140 }
141
142 RTLdrClose(hUser32);
143 }
144
145 if (RT_SUCCESS(rc))
146 {
147 LogFunc(("New Clipboard API enabled\n"));
148 }
149 else
150 {
151 RT_BZERO(pAPI, sizeof(VBOXCLIPBOARDWINAPINEW));
152 LogFunc(("New Clipboard API not available; rc=%Rrc\n", rc));
153 }
154
155 return rc;
156}
157
158/**
159 * Returns if the new clipboard API is available or not.
160 *
161 * @returns @c true if the new API is available, or @c false if not.
162 * @param pAPI Structure used for checking if the new clipboard API is available or not.
163 */
164bool VBoxClipboardWinIsNewAPI(PVBOXCLIPBOARDWINAPINEW pAPI)
165{
166 if (!pAPI)
167 return false;
168 return pAPI->pfnAddClipboardFormatListener != NULL;
169}
170
171/**
172 * Adds ourselves into the chain of cliboard listeners.
173 *
174 * @returns VBox status code.
175 * @param pCtx Windows clipboard context to use to add ourselves.
176 */
177int VBoxClipboardWinAddToCBChain(PVBOXCLIPBOARDWINCTX pCtx)
178{
179 const PVBOXCLIPBOARDWINAPINEW pAPI = &pCtx->newAPI;
180
181 BOOL fRc;
182 if (VBoxClipboardWinIsNewAPI(pAPI))
183 {
184 fRc = pAPI->pfnAddClipboardFormatListener(pCtx->hWnd);
185 }
186 else
187 {
188 pCtx->hWndNextInChain = SetClipboardViewer(pCtx->hWnd);
189 fRc = pCtx->hWndNextInChain != NULL;
190 }
191
192 int rc = VINF_SUCCESS;
193
194 if (!fRc)
195 {
196 const DWORD dwLastErr = GetLastError();
197 rc = RTErrConvertFromWin32(dwLastErr);
198 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
199 }
200
201 return rc;
202}
203
204/**
205 * Remove ourselves from the chain of cliboard listeners
206 *
207 * @returns VBox status code.
208 * @param pCtx Windows clipboard context to use to remove ourselves.
209 */
210int VBoxClipboardWinRemoveFromCBChain(PVBOXCLIPBOARDWINCTX pCtx)
211{
212 if (!pCtx->hWnd)
213 return VINF_SUCCESS;
214
215 const PVBOXCLIPBOARDWINAPINEW pAPI = &pCtx->newAPI;
216
217 BOOL fRc;
218 if (VBoxClipboardWinIsNewAPI(pAPI))
219 {
220 fRc = pAPI->pfnRemoveClipboardFormatListener(pCtx->hWnd);
221 }
222 else
223 {
224 fRc = ChangeClipboardChain(pCtx->hWnd, pCtx->hWndNextInChain);
225 if (fRc)
226 pCtx->hWndNextInChain = NULL;
227 }
228
229 int rc = VINF_SUCCESS;
230
231 if (!fRc)
232 {
233 const DWORD dwLastErr = GetLastError();
234 rc = RTErrConvertFromWin32(dwLastErr);
235 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
236 }
237
238 return rc;
239}
240
241/**
242 * Callback which is invoked when we have successfully pinged ourselves down the
243 * clipboard chain. We simply unset a boolean flag to say that we are responding.
244 * There is a race if a ping returns after the next one is initiated, but nothing
245 * very bad is likely to happen.
246 *
247 * @param hWnd Window handle to use for this callback. Not used currently.
248 * @param uMsg Message to handle. Not used currently.
249 * @param dwData Pointer to user-provided data. Contains our Windows clipboard context.
250 * @param lResult Additional data to pass. Not used currently.
251 */
252VOID CALLBACK VBoxClipboardWinChainPingProc(HWND hWnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult)
253{
254 RT_NOREF(hWnd);
255 RT_NOREF(uMsg);
256 RT_NOREF(lResult);
257
258 /** @todo r=andy Why not using SetWindowLongPtr for keeping the context? */
259 PVBOXCLIPBOARDWINCTX pCtx = (PVBOXCLIPBOARDWINCTX)dwData;
260 AssertPtrReturnVoid(pCtx);
261
262 pCtx->oldAPI.fCBChainPingInProcess = FALSE;
263}
264
265/**
266 * Retrieves all supported clipboard formats of a specific clipboard.
267 *
268 * @returns VBox status code.
269 * @param pCtx Windows clipboard context to retrieve formats for.
270 * @param puFormats Where to store the retrieved formats of type VBOX_SHARED_CLIPBOARD_FMT_ (bitmask).
271 */
272int VBoxClipboardWinGetFormats(PVBOXCLIPBOARDWINCTX pCtx, uint32_t *puFormats)
273{
274 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
275 AssertPtrReturn(puFormats, VERR_INVALID_POINTER);
276
277 if (!pCtx)
278 {
279 *puFormats = 0;
280 return VINF_SUCCESS;
281 }
282
283 uint32_t uFormats = 0;
284
285 /* Query list of available formats and report to host. */
286 int rc = VBoxClipboardWinOpen(pCtx->hWnd);
287 if (RT_SUCCESS(rc))
288 {
289 UINT uCurFormat = 0;
290 while ((uCurFormat = EnumClipboardFormats(uCurFormat)) != 0)
291 {
292 LogFlowFunc(("uFormat = 0x%08X\n", uCurFormat));
293 switch (uCurFormat)
294 {
295 case CF_UNICODETEXT:
296 case CF_TEXT:
297 uFormats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
298 break;
299
300 case CF_DIB:
301 case CF_BITMAP:
302 uFormats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
303 break;
304
305#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
306 case CF_HDROP:
307 uFormats |= VBOX_SHARED_CLIPBOARD_FMT_URI_LIST;
308 break;
309#endif
310 default:
311 {
312 if (uCurFormat >= 0xC000) /** @todo r=andy Find a define for this. */
313 {
314 TCHAR szFormatName[256]; /** @todo r=andy Is this enough? */
315 int cActual = GetClipboardFormatName(uCurFormat, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
316 if (cActual)
317 {
318 if (strcmp (szFormatName, "HTML Format") == 0)
319 {
320 uFormats |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
321 }
322 }
323 }
324 break;
325 }
326 }
327 }
328
329 int rc2 = VBoxClipboardWinClose();
330 AssertRC(rc2);
331 }
332
333 if (RT_FAILURE(rc))
334 {
335 LogFunc(("Failed with rc=%Rrc\n", rc));
336 }
337 else
338 *puFormats = uFormats;
339
340 return rc;
341}
342
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