VirtualBox

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

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

Shared Clipboard/URI: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.5 KB
Line 
1/* $Id: clipboard-win.cpp 78501 2019-05-14 11:36:25Z 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#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
25# include <iprt/utf16.h>
26#endif
27
28#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
29#include <VBox/log.h>
30
31#include <VBox/GuestHost/SharedClipboard.h>
32#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
33# include <VBox/GuestHost/SharedClipboard-uri.h>
34#endif
35#include <VBox/GuestHost/SharedClipboard-win.h>
36#include <VBox/GuestHost/clipboard-helper.h>
37
38/**
39 * Opens the clipboard of a specific window.
40 *
41 * @returns VBox status code.
42 * @param hWnd Handle of window to open clipboard for.
43 */
44int VBoxClipboardWinOpen(HWND hWnd)
45{
46 /* "OpenClipboard fails if another window has the clipboard open."
47 * So try a few times and wait up to 1 second.
48 */
49 BOOL fOpened = FALSE;
50
51 LogFlowFunc(("hWnd=%p\n", hWnd));
52
53 int i = 0;
54 for (;;)
55 {
56 if (OpenClipboard(hWnd))
57 {
58 fOpened = TRUE;
59 break;
60 }
61
62 if (i >= 10) /* sleep interval = [1..512] ms */
63 break;
64
65 RTThreadSleep(1 << i);
66 ++i;
67 }
68
69#ifdef LOG_ENABLED
70 if (i > 0)
71 LogFlowFunc(("%d times tried to open clipboard\n", i + 1));
72#endif
73
74 int rc;
75 if (fOpened)
76 rc = VINF_SUCCESS;
77 else
78 {
79 const DWORD dwLastErr = GetLastError();
80 rc = RTErrConvertFromWin32(dwLastErr);
81 LogFunc(("Failed to open clipboard, rc=%Rrc (0x%x)\n", rc, dwLastErr));
82 }
83
84 return rc;
85}
86
87/**
88 * Closes the clipboard for the current thread.
89 *
90 * @returns VBox status code.
91 */
92int VBoxClipboardWinClose(void)
93{
94 int rc;
95
96 LogFlowFuncEnter();
97
98 const BOOL fRc = CloseClipboard();
99 if (RT_UNLIKELY(!fRc))
100 {
101 const DWORD dwLastErr = GetLastError();
102 if (dwLastErr == ERROR_CLIPBOARD_NOT_OPEN)
103 rc = VERR_INVALID_STATE;
104 else
105 rc = RTErrConvertFromWin32(dwLastErr);
106
107 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
108 }
109 else
110 rc = VINF_SUCCESS;
111
112 return rc;
113}
114
115/**
116 * Clears the clipboard for the current thread.
117 *
118 * @returns VBox status code.
119 */
120int VBoxClipboardWinClear(void)
121{
122 int rc;
123
124 LogFlowFuncEnter();
125
126 const BOOL fRc = EmptyClipboard();
127 if (RT_UNLIKELY(!fRc))
128 {
129 const DWORD dwLastErr = GetLastError();
130 if (dwLastErr == ERROR_CLIPBOARD_NOT_OPEN)
131 rc = VERR_INVALID_STATE;
132 else
133 rc = RTErrConvertFromWin32(dwLastErr);
134
135 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
136 }
137 else
138 rc = VINF_SUCCESS;
139
140 return rc;
141}
142
143/**
144 * Checks and initializes function pointer which are required for using
145 * the new clipboard API.
146 *
147 * @returns VBox status code.
148 * @param pAPI Where to store the retrieved function pointers.
149 * Will be set to NULL if the new API is not available.
150 */
151int VBoxClipboardWinCheckAndInitNewAPI(PVBOXCLIPBOARDWINAPINEW pAPI)
152{
153 RTLDRMOD hUser32 = NIL_RTLDRMOD;
154 int rc = RTLdrLoadSystem("User32.dll", /* fNoUnload = */ true, &hUser32);
155 if (RT_SUCCESS(rc))
156 {
157 rc = RTLdrGetSymbol(hUser32, "AddClipboardFormatListener", (void **)&pAPI->pfnAddClipboardFormatListener);
158 if (RT_SUCCESS(rc))
159 {
160 rc = RTLdrGetSymbol(hUser32, "RemoveClipboardFormatListener", (void **)&pAPI->pfnRemoveClipboardFormatListener);
161 }
162
163 RTLdrClose(hUser32);
164 }
165
166 if (RT_SUCCESS(rc))
167 {
168 LogFunc(("New Clipboard API enabled\n"));
169 }
170 else
171 {
172 RT_BZERO(pAPI, sizeof(VBOXCLIPBOARDWINAPINEW));
173 LogFunc(("New Clipboard API not available; rc=%Rrc\n", rc));
174 }
175
176 return rc;
177}
178
179/**
180 * Returns if the new clipboard API is available or not.
181 *
182 * @returns @c true if the new API is available, or @c false if not.
183 * @param pAPI Structure used for checking if the new clipboard API is available or not.
184 */
185bool VBoxClipboardWinIsNewAPI(PVBOXCLIPBOARDWINAPINEW pAPI)
186{
187 if (!pAPI)
188 return false;
189 return pAPI->pfnAddClipboardFormatListener != NULL;
190}
191
192/**
193 * Adds ourselves into the chain of cliboard listeners.
194 *
195 * @returns VBox status code.
196 * @param pCtx Windows clipboard context to use to add ourselves.
197 */
198int VBoxClipboardWinAddToCBChain(PVBOXCLIPBOARDWINCTX pCtx)
199{
200 const PVBOXCLIPBOARDWINAPINEW pAPI = &pCtx->newAPI;
201
202 BOOL fRc;
203 if (VBoxClipboardWinIsNewAPI(pAPI))
204 {
205 fRc = pAPI->pfnAddClipboardFormatListener(pCtx->hWnd);
206 }
207 else
208 {
209 pCtx->hWndNextInChain = SetClipboardViewer(pCtx->hWnd);
210 fRc = pCtx->hWndNextInChain != NULL;
211 }
212
213 int rc = VINF_SUCCESS;
214
215 if (!fRc)
216 {
217 const DWORD dwLastErr = GetLastError();
218 rc = RTErrConvertFromWin32(dwLastErr);
219 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
220 }
221
222 return rc;
223}
224
225/**
226 * Remove ourselves from the chain of cliboard listeners
227 *
228 * @returns VBox status code.
229 * @param pCtx Windows clipboard context to use to remove ourselves.
230 */
231int VBoxClipboardWinRemoveFromCBChain(PVBOXCLIPBOARDWINCTX pCtx)
232{
233 if (!pCtx->hWnd)
234 return VINF_SUCCESS;
235
236 const PVBOXCLIPBOARDWINAPINEW pAPI = &pCtx->newAPI;
237
238 BOOL fRc;
239 if (VBoxClipboardWinIsNewAPI(pAPI))
240 {
241 fRc = pAPI->pfnRemoveClipboardFormatListener(pCtx->hWnd);
242 }
243 else
244 {
245 fRc = ChangeClipboardChain(pCtx->hWnd, pCtx->hWndNextInChain);
246 if (fRc)
247 pCtx->hWndNextInChain = NULL;
248 }
249
250 int rc = VINF_SUCCESS;
251
252 if (!fRc)
253 {
254 const DWORD dwLastErr = GetLastError();
255 rc = RTErrConvertFromWin32(dwLastErr);
256 LogFunc(("Failed with %Rrc (0x%x)\n", rc, dwLastErr));
257 }
258
259 return rc;
260}
261
262/**
263 * Callback which is invoked when we have successfully pinged ourselves down the
264 * clipboard chain. We simply unset a boolean flag to say that we are responding.
265 * There is a race if a ping returns after the next one is initiated, but nothing
266 * very bad is likely to happen.
267 *
268 * @param hWnd Window handle to use for this callback. Not used currently.
269 * @param uMsg Message to handle. Not used currently.
270 * @param dwData Pointer to user-provided data. Contains our Windows clipboard context.
271 * @param lResult Additional data to pass. Not used currently.
272 */
273VOID CALLBACK VBoxClipboardWinChainPingProc(HWND hWnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult)
274{
275 RT_NOREF(hWnd);
276 RT_NOREF(uMsg);
277 RT_NOREF(lResult);
278
279 /** @todo r=andy Why not using SetWindowLongPtr for keeping the context? */
280 PVBOXCLIPBOARDWINCTX pCtx = (PVBOXCLIPBOARDWINCTX)dwData;
281 AssertPtrReturnVoid(pCtx);
282
283 pCtx->oldAPI.fCBChainPingInProcess = FALSE;
284}
285
286/**
287 * Converts a (registered or standard) Windows clipboard format to a VBox clipboard format.
288 *
289 * @returns Converted VBox clipboard format, or VBOX_SHARED_CLIPBOARD_FMT_NONE if not found.
290 * @param uFormat Windows clipboard format to convert.
291 */
292VBOXCLIPBOARDFORMAT VBoxClipboardWinClipboardFormatToVBox(UINT uFormat)
293{
294 /* Insert the requested clipboard format data into the clipboard. */
295 VBOXCLIPBOARDFORMAT vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_NONE;
296
297 switch (uFormat)
298 {
299 case CF_UNICODETEXT:
300 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
301 break;
302
303 case CF_DIB:
304 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
305 break;
306
307#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
308 /* Handles file system entries which are locally present
309 * on source for transferring to the target. */
310 case CF_HDROP:
311 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_URI_LIST;
312 break;
313#endif
314
315 default:
316 if (uFormat >= 0xC000) /** Formats registered with RegisterClipboardFormat() start at this index. */
317 {
318 TCHAR szFormatName[256]; /** @todo r=andy Do we need Unicode support here as well? */
319 int cActual = GetClipboardFormatName(uFormat, szFormatName, sizeof(szFormatName) / sizeof(TCHAR));
320 if (cActual)
321 {
322 LogFlowFunc(("uFormat=%u -> szFormatName=%s\n", uFormat, szFormatName));
323
324 if (RTStrCmp(szFormatName, VBOX_CLIPBOARD_WIN_REGFMT_HTML) == 0)
325 vboxFormat = VBOX_SHARED_CLIPBOARD_FMT_HTML;
326 }
327 }
328 break;
329 }
330
331 LogFlowFunc(("uFormat=%u -> vboxFormat=0x%x\n", uFormat, vboxFormat));
332 return vboxFormat;
333}
334
335/**
336 * Retrieves all supported clipboard formats of a specific clipboard.
337 *
338 * @returns VBox status code.
339 * @param pCtx Windows clipboard context to retrieve formats for.
340 * @param pfFormats Where to store the retrieved formats of type VBOX_SHARED_CLIPBOARD_FMT_ (bitmask).
341 */
342int VBoxClipboardWinGetFormats(PVBOXCLIPBOARDWINCTX pCtx, PVBOXCLIPBOARDFORMATS pfFormats)
343{
344 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
345 AssertPtrReturn(pfFormats, VERR_INVALID_POINTER);
346
347 VBOXCLIPBOARDFORMATS fFormats = VBOX_SHARED_CLIPBOARD_FMT_NONE;
348
349 /* Query list of available formats and report to host. */
350 int rc = VBoxClipboardWinOpen(pCtx->hWnd);
351 if (RT_SUCCESS(rc))
352 {
353 UINT uCurFormat = 0; /* Must be set to zero for EnumClipboardFormats(). */
354 while ((uCurFormat = EnumClipboardFormats(uCurFormat)) != 0)
355 fFormats |= VBoxClipboardWinClipboardFormatToVBox(uCurFormat);
356
357 int rc2 = VBoxClipboardWinClose();
358 AssertRC(rc2);
359 }
360
361 if (RT_FAILURE(rc))
362 {
363 LogFunc(("Failed with rc=%Rrc\n", rc));
364 }
365 else
366 {
367 LogFlowFunc(("pfFormats=0x%08X\n", pfFormats));
368 *pfFormats = fFormats;
369 }
370
371 return rc;
372}
373
374/**
375 * Extracts a field value from CF_HTML data.
376 *
377 * @returns VBox status code.
378 * @param pszSrc source in CF_HTML format.
379 * @param pszOption Name of CF_HTML field.
380 * @param puValue Where to return extracted value of CF_HTML field.
381 */
382int VBoxClipboardWinGetCFHTMLHeaderValue(const char *pszSrc, const char *pszOption, uint32_t *puValue)
383{
384 AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
385 AssertPtrReturn(pszOption, VERR_INVALID_POINTER);
386
387 int rc = VERR_INVALID_PARAMETER;
388
389 const char *pszOptionValue = RTStrStr(pszSrc, pszOption);
390 if (pszOptionValue)
391 {
392 size_t cchOption = strlen(pszOption);
393 Assert(cchOption);
394
395 rc = RTStrToUInt32Ex(pszOptionValue + cchOption, NULL, 10, puValue);
396 }
397 return rc;
398}
399
400/**
401 * Check that the source string contains CF_HTML struct.
402 *
403 * @returns @c true if the @a pszSource string is in CF_HTML format.
404 * @param pszSource Source string to check.
405 */
406bool VBoxClipboardWinIsCFHTML(const char *pszSource)
407{
408 return RTStrStr(pszSource, "Version:") != NULL
409 && RTStrStr(pszSource, "StartHTML:") != NULL;
410}
411
412/**
413 * Converts clipboard data from CF_HTML format to MIME clipboard format.
414 *
415 * Returns allocated buffer that contains html converted to text/html mime type
416 *
417 * @returns VBox status code.
418 * @param pszSource The input.
419 * @param cch The length of the input.
420 * @param ppszOutput Where to return the result. Free using RTMemFree.
421 * @param pcbOutput Where to the return length of the result (bytes/chars).
422 */
423int VBoxClipboardWinConvertCFHTMLToMIME(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pcbOutput)
424{
425 Assert(pszSource);
426 Assert(cch);
427 Assert(ppszOutput);
428 Assert(pcbOutput);
429
430 uint32_t offStart;
431 int rc = VBoxClipboardWinGetCFHTMLHeaderValue(pszSource, "StartFragment:", &offStart);
432 if (RT_SUCCESS(rc))
433 {
434 uint32_t offEnd;
435 rc = VBoxClipboardWinGetCFHTMLHeaderValue(pszSource, "EndFragment:", &offEnd);
436 if (RT_SUCCESS(rc))
437 {
438 if ( offStart > 0
439 && offEnd > 0
440 && offEnd > offStart
441 && offEnd <= cch)
442 {
443 uint32_t cchSubStr = offEnd - offStart;
444 char *pszResult = (char *)RTMemAlloc(cchSubStr + 1);
445 if (pszResult)
446 {
447 rc = RTStrCopyEx(pszResult, cchSubStr + 1, pszSource + offStart, cchSubStr);
448 if (RT_SUCCESS(rc))
449 {
450 *ppszOutput = pszResult;
451 *pcbOutput = (uint32_t)(cchSubStr + 1);
452 rc = VINF_SUCCESS;
453 }
454 else
455 {
456 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
457 RTMemFree(pszResult);
458 }
459 }
460 else
461 {
462 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment\n"));
463 rc = VERR_NO_MEMORY;
464 }
465 }
466 else
467 {
468 LogRelFlowFunc(("Error: CF_HTML out of bounds - offStart=%#x offEnd=%#x cch=%#x\n", offStart, offEnd, cch));
469 rc = VERR_INVALID_PARAMETER;
470 }
471 }
472 else
473 {
474 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
475 rc = VERR_INVALID_PARAMETER;
476 }
477 }
478 else
479 {
480 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected StartFragment. rc = %Rrc\n", rc));
481 rc = VERR_INVALID_PARAMETER;
482 }
483
484 return rc;
485}
486
487/**
488 * Converts source UTF-8 MIME HTML clipboard data to UTF-8 CF_HTML format.
489 *
490 * This is just encapsulation work, slapping a header on the data.
491 *
492 * It allocates [..]
493 *
494 * Calculations:
495 * Header length = format Length + (2*(10 - 5('%010d'))('digits')) - 2('%s') = format length + 8
496 * EndHtml = Header length + fragment length
497 * StartHtml = 105(constant)
498 * StartFragment = 141(constant) may vary if the header html content will be extended
499 * EndFragment = Header length + fragment length - 38(ending length)
500 *
501 * @param pszSource Source buffer that contains utf-16 string in mime html format
502 * @param cb Size of source buffer in bytes
503 * @param ppszOutput Where to return the allocated output buffer to put converted UTF-8
504 * CF_HTML clipboard data. This function allocates memory for this.
505 * @param pcbOutput Where to return the size of allocated result buffer in bytes/chars, including zero terminator
506 *
507 * @note output buffer should be free using RTMemFree()
508 * @note Everything inside of fragment can be UTF8. Windows allows it. Everything in header should be Latin1.
509 */
510int VBoxClipboardWinConvertMIMEToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput)
511{
512 Assert(ppszOutput);
513 Assert(pcbOutput);
514 Assert(pszSource);
515 Assert(cb);
516
517 /* construct CF_HTML formatted string */
518 char *pszResult = NULL;
519 size_t cchFragment;
520 int rc = RTStrNLenEx(pszSource, cb, &cchFragment);
521 if (!RT_SUCCESS(rc))
522 {
523 LogRelFlowFunc(("Error: invalid source fragment. rc = %Rrc\n"));
524 return VERR_INVALID_PARAMETER;
525 }
526
527 /*
528 @StartHtml - pos before <html>
529 @EndHtml - whole size of text excluding ending zero char
530 @StartFragment - pos after <!--StartFragment-->
531 @EndFragment - pos before <!--EndFragment-->
532 @note: all values includes CR\LF inserted into text
533 Calculations:
534 Header length = format Length + (3*6('digits')) - 2('%s') = format length + 16 (control value - 183)
535 EndHtml = Header length + fragment length
536 StartHtml = 105(constant)
537 StartFragment = 143(constant)
538 EndFragment = Header length + fragment length - 40(ending length)
539 */
540 static const char s_szFormatSample[] =
541 /* 0: */ "Version:1.0\r\n"
542 /* 13: */ "StartHTML:000000101\r\n"
543 /* 34: */ "EndHTML:%0000009u\r\n" // END HTML = Header length + fragment length
544 /* 53: */ "StartFragment:000000137\r\n"
545 /* 78: */ "EndFragment:%0000009u\r\n"
546 /* 101: */ "<html>\r\n"
547 /* 109: */ "<body>\r\n"
548 /* 117: */ "<!--StartFragment-->"
549 /* 137: */ "%s"
550 /* 137+2: */ "<!--EndFragment-->\r\n"
551 /* 157+2: */ "</body>\r\n"
552 /* 166+2: */ "</html>\r\n";
553 /* 175+2: */
554 AssertCompile(sizeof(s_szFormatSample) == 175 + 2 + 1);
555
556 /* calculate parameters of CF_HTML header */
557 size_t cchHeader = sizeof(s_szFormatSample) - 1;
558 size_t offEndHtml = cchHeader + cchFragment;
559 size_t offEndFragment = cchHeader + cchFragment - 38; /* 175-137 = 38 */
560 pszResult = (char *)RTMemAlloc(offEndHtml + 1);
561 if (pszResult == NULL)
562 {
563 LogRelFlowFunc(("Error: Cannot allocate memory for result buffer. rc = %Rrc\n"));
564 return VERR_NO_MEMORY;
565 }
566
567 /* format result CF_HTML string */
568 size_t cchFormatted = RTStrPrintf(pszResult, offEndHtml + 1,
569 s_szFormatSample, offEndHtml, offEndFragment, pszSource);
570 Assert(offEndHtml == cchFormatted); NOREF(cchFormatted);
571
572#ifdef VBOX_STRICT
573 /* Control calculations. check consistency.*/
574 static const char s_szStartFragment[] = "<!--StartFragment-->";
575 static const char s_szEndFragment[] = "<!--EndFragment-->";
576
577 /* check 'StartFragment:' value */
578 const char *pszRealStartFragment = RTStrStr(pszResult, s_szStartFragment);
579 Assert(&pszRealStartFragment[sizeof(s_szStartFragment) - 1] - pszResult == 137);
580
581 /* check 'EndFragment:' value */
582 const char *pszRealEndFragment = RTStrStr(pszResult, s_szEndFragment);
583 Assert((size_t)(pszRealEndFragment - pszResult) == offEndFragment);
584#endif
585
586 *ppszOutput = pszResult;
587 *pcbOutput = (uint32_t)cchFormatted + 1;
588 Assert(*pcbOutput == cchFormatted + 1);
589
590 return VINF_SUCCESS;
591}
592
593#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
594/**
595 * Converts a DROPFILES (HDROP) structure to a string list, separated by \r\n.
596 *
597 * @returns VBox status code.
598 * @param pDropFiles Pointer to DROPFILES structure to convert.
599 * @param ppvData Where to return the converted (allocated) data on success.
600 * @param pcbData Size (in bytes) of the allocated data returned.
601 */
602int VBoxClipboardWinDropFilesToStringList(DROPFILES *pDropFiles, void **ppvData, size_t *pcbData)
603{
604 AssertPtrReturn(pDropFiles, VERR_INVALID_POINTER);
605 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
606 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
607
608 /* Do we need to do Unicode stuff? */
609 const bool fUnicode = RT_BOOL(pDropFiles->fWide);
610
611 /* Get the offset of the file list. */
612 Assert(pDropFiles->pFiles >= sizeof(DROPFILES));
613
614 /* Note: This is *not* pDropFiles->pFiles! DragQueryFile only
615 * will work with the plain storage medium pointer! */
616 HDROP hDrop = (HDROP)(pDropFiles);
617
618 int rc = VINF_SUCCESS;
619
620 /* First, get the file count. */
621 /** @todo Does this work on Windows 2000 / NT4? */
622 char *pszFiles = NULL;
623 uint32_t cchFiles = 0;
624 UINT cFiles = DragQueryFile(hDrop, UINT32_MAX /* iFile */, NULL /* lpszFile */, 0 /* cchFile */);
625
626 LogFlowFunc(("Got %RU16 file(s), fUnicode=%RTbool\n", cFiles, fUnicode));
627
628 for (UINT i = 0; i < cFiles; i++)
629 {
630 UINT cchFile = DragQueryFile(hDrop, i /* File index */, NULL /* Query size first */, 0 /* cchFile */);
631 Assert(cchFile);
632
633 if (RT_FAILURE(rc))
634 break;
635
636 char *pszFileUtf8 = NULL; /* UTF-8 version. */
637 UINT cchFileUtf8 = 0;
638 if (fUnicode)
639 {
640 /* Allocate enough space (including terminator). */
641 WCHAR *pwszFile = (WCHAR *)RTMemAlloc((cchFile + 1) * sizeof(WCHAR));
642 if (pwszFile)
643 {
644 const UINT cwcFileUtf16 = DragQueryFileW(hDrop, i /* File index */,
645 pwszFile, cchFile + 1 /* Include terminator */);
646
647 AssertMsg(cwcFileUtf16 == cchFile, ("cchFileUtf16 (%RU16) does not match cchFile (%RU16)\n",
648 cwcFileUtf16, cchFile));
649 RT_NOREF(cwcFileUtf16);
650
651 rc = RTUtf16ToUtf8(pwszFile, &pszFileUtf8);
652 if (RT_SUCCESS(rc))
653 {
654 cchFileUtf8 = (UINT)strlen(pszFileUtf8);
655 Assert(cchFileUtf8);
656 }
657
658 RTMemFree(pwszFile);
659 }
660 else
661 rc = VERR_NO_MEMORY;
662 }
663 else /* ANSI */
664 {
665 /* Allocate enough space (including terminator). */
666 pszFileUtf8 = (char *)RTMemAlloc((cchFile + 1) * sizeof(char));
667 if (pszFileUtf8)
668 {
669 cchFileUtf8 = DragQueryFileA(hDrop, i /* File index */,
670 pszFileUtf8, cchFile + 1 /* Include terminator */);
671
672 AssertMsg(cchFileUtf8 == cchFile, ("cchFileUtf8 (%RU16) does not match cchFile (%RU16)\n",
673 cchFileUtf8, cchFile));
674 }
675 else
676 rc = VERR_NO_MEMORY;
677 }
678
679 if (RT_SUCCESS(rc))
680 {
681 LogFlowFunc(("\tFile: %s (cchFile=%RU16)\n", pszFileUtf8, cchFileUtf8));
682
683 LogRel(("DnD: Adding guest file '%s'\n", pszFileUtf8));
684
685 rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, pszFileUtf8, cchFileUtf8);
686 if (RT_SUCCESS(rc))
687 cchFiles += cchFileUtf8;
688 }
689 else
690 LogFunc(("Error handling file entry #%u, rc=%Rrc\n", i, rc));
691
692 if (pszFileUtf8)
693 RTStrFree(pszFileUtf8);
694
695 if (RT_FAILURE(rc))
696 break;
697
698 /* Add separation between filenames.
699 * Note: Also do this for the last element of the list. */
700 rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, "\r\n", 2 /* Bytes */);
701 if (RT_SUCCESS(rc))
702 cchFiles += 2; /* Include \r\n */
703 }
704
705 if (RT_SUCCESS(rc))
706 {
707 cchFiles += 1; /* Add string termination. */
708 uint32_t cbFiles = cchFiles * sizeof(char);
709
710 LogFlowFunc(("cFiles=%u, cchFiles=%RU32, cbFiles=%RU32, pszFiles=0x%p\n",
711 cFiles, cchFiles, cbFiles, pszFiles));
712
713 /* Translate the list into URI elements. */
714 SharedClipboardURIList lstURI;
715 rc = lstURI.AppendNativePathsFromList(pszFiles, cbFiles,
716 SHAREDCLIPBOARDURILIST_FLAGS_ABSOLUTE_PATHS);
717 if (RT_SUCCESS(rc))
718 {
719 RTCString strRoot = lstURI.GetRootEntries();
720 size_t cbRoot = strRoot.length() + 1; /* Include termination */
721
722 void *pvData = RTMemAlloc(cbRoot);
723 if (pvData)
724 {
725 memcpy(pvData, strRoot.c_str(), cbRoot);
726
727 *ppvData = pvData;
728 *pcbData = cbRoot;
729 }
730 else
731 rc = VERR_NO_MEMORY;
732 }
733 }
734
735 LogFlowFunc(("Building CF_HDROP list rc=%Rrc, pszFiles=0x%p, cFiles=%RU16, cchFiles=%RU32\n",
736 rc, pszFiles, cFiles, cchFiles));
737
738 if (pszFiles)
739 RTStrFree(pszFiles);
740
741 return rc;
742}
743#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
744
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