23#include <freerdp/config.h>
33#include <winpr/assert.h>
34#include <winpr/library.h>
37#include <winpr/tchar.h>
38#include <winpr/stream.h>
40#include <freerdp/log.h>
41#include <freerdp/client/cliprdr.h>
45#include "wf_cliprdr.h"
47#define TAG CLIENT_TAG("windows")
49#ifdef WITH_DEBUG_CLIPRDR
50#define DEBUG_CLIPRDR(...) WLog_DBG(TAG, __VA_ARGS__)
52#define DEBUG_CLIPRDR(...) \
58typedef BOOL(WINAPI* fnAddClipboardFormatListener)(HWND hwnd);
59typedef BOOL(WINAPI* fnRemoveClipboardFormatListener)(HWND hwnd);
60typedef BOOL(WINAPI* fnGetUpdatedClipboardFormats)(PUINT lpuiFormats, UINT cFormats,
65 UINT32 remote_format_id;
66 UINT32 local_format_id;
72 IEnumFORMATETC iEnumFORMATETC;
77 FORMATETC* m_pFormatEtc;
78} CliprdrEnumFORMATETC;
94 IDataObject iDataObject;
97 FORMATETC* m_pFormatEtc;
98 STGMEDIUM* m_pStgMedium;
108 rdpChannels* channels;
109 CliprdrClientContext* context;
116 formatMapping* format_mappings;
118 UINT32 requestedFormatId;
123 HANDLE response_data_event;
125 LPDATAOBJECT data_obj;
131 size_t file_array_size;
138 fnAddClipboardFormatListener AddClipboardFormatListener;
139 fnRemoveClipboardFormatListener RemoveClipboardFormatListener;
140 fnGetUpdatedClipboardFormats GetUpdatedClipboardFormats;
143#define WM_CLIPRDR_MESSAGE (WM_USER + 156)
144#define OLE_SETCLIPBOARD 1
146static BOOL wf_create_file_obj(wfClipboard* cliprdrrdr, IDataObject** ppDataObject);
147static void wf_destroy_file_obj(IDataObject* instance);
148static UINT32 get_remote_format_id(wfClipboard* clipboard, UINT32 local_format);
149static UINT cliprdr_send_data_request(wfClipboard* clipboard, UINT32 format);
150static UINT cliprdr_send_lock(wfClipboard* clipboard);
151static UINT cliprdr_send_unlock(wfClipboard* clipboard);
152static UINT cliprdr_send_request_filecontents(wfClipboard* clipboard,
const void* streamid,
153 ULONG index, UINT32 flag, UINT64 position,
156static void CliprdrDataObject_Delete(CliprdrDataObject* instance);
158static CliprdrEnumFORMATETC* CliprdrEnumFORMATETC_New(ULONG nFormats, FORMATETC* pFormatEtc);
159static void CliprdrEnumFORMATETC_Delete(CliprdrEnumFORMATETC* instance);
161static void CliprdrStream_Delete(CliprdrStream* instance);
163static BOOL try_open_clipboard(HWND hwnd)
165 for (
size_t x = 0; x < 10; x++)
167 if (OpenClipboard(hwnd))
178static HRESULT STDMETHODCALLTYPE CliprdrStream_QueryInterface(IStream* This, REFIID riid,
181 if (IsEqualIID(riid, &IID_IStream) || IsEqualIID(riid, &IID_IUnknown))
183 IStream_AddRef(This);
190 return E_NOINTERFACE;
194static ULONG STDMETHODCALLTYPE CliprdrStream_AddRef(IStream* This)
196 CliprdrStream* instance = (CliprdrStream*)This;
201 return InterlockedIncrement(&instance->m_lRefCount);
204static ULONG STDMETHODCALLTYPE CliprdrStream_Release(IStream* This)
207 CliprdrStream* instance = (CliprdrStream*)This;
212 count = InterlockedDecrement(&instance->m_lRefCount);
216 CliprdrStream_Delete(instance);
225static HRESULT STDMETHODCALLTYPE CliprdrStream_Read(IStream* This,
void* pv, ULONG cb,
228 CliprdrStream* instance = (CliprdrStream*)This;
230 if (!pv || !pcbRead || !instance)
233 wfClipboard* clipboard = (wfClipboard*)instance->m_pData;
236 if (instance->m_lOffset.QuadPart >= instance->m_lSize.QuadPart)
240 cliprdr_send_request_filecontents(clipboard, (
void*)This, instance->m_lIndex,
241 FILECONTENTS_RANGE, instance->m_lOffset.QuadPart, cb);
246 if (clipboard->req_fsize > cb)
249 if (clipboard->req_fdata)
251 CopyMemory(pv, clipboard->req_fdata, clipboard->req_fsize);
252 free(clipboard->req_fdata);
255 *pcbRead = clipboard->req_fsize;
256 instance->m_lOffset.QuadPart += clipboard->req_fsize;
258 if (clipboard->req_fsize < cb)
264static HRESULT STDMETHODCALLTYPE CliprdrStream_Write(IStream* This,
const void* pv, ULONG cb,
271 return STG_E_ACCESSDENIED;
274static HRESULT STDMETHODCALLTYPE CliprdrStream_Seek(IStream* This,
LARGE_INTEGER dlibMove,
278 CliprdrStream* instance = (CliprdrStream*)This;
283 newoffset = instance->m_lOffset.QuadPart;
287 case STREAM_SEEK_SET:
288 newoffset = dlibMove.QuadPart;
291 case STREAM_SEEK_CUR:
292 newoffset += dlibMove.QuadPart;
295 case STREAM_SEEK_END:
296 newoffset = instance->m_lSize.QuadPart + dlibMove.QuadPart;
303 if (newoffset < 0 || newoffset >= instance->m_lSize.QuadPart)
306 instance->m_lOffset.QuadPart = newoffset;
309 plibNewPosition->QuadPart = instance->m_lOffset.QuadPart;
314static HRESULT STDMETHODCALLTYPE CliprdrStream_SetSize(IStream* This,
ULARGE_INTEGER libNewSize)
321static HRESULT STDMETHODCALLTYPE CliprdrStream_CopyTo(IStream* This, IStream* pstm,
333static HRESULT STDMETHODCALLTYPE CliprdrStream_Commit(IStream* This, DWORD grfCommitFlags)
336 (void)grfCommitFlags;
340static HRESULT STDMETHODCALLTYPE CliprdrStream_Revert(IStream* This)
346static HRESULT STDMETHODCALLTYPE CliprdrStream_LockRegion(IStream* This,
ULARGE_INTEGER libOffset,
356static HRESULT STDMETHODCALLTYPE CliprdrStream_UnlockRegion(IStream* This,
ULARGE_INTEGER libOffset,
366static HRESULT STDMETHODCALLTYPE CliprdrStream_Stat(IStream* This, STATSTG* pstatstg,
369 CliprdrStream* instance = (CliprdrStream*)This;
374 if (pstatstg ==
nullptr)
375 return STG_E_INVALIDPOINTER;
377 ZeroMemory(pstatstg,
sizeof(STATSTG));
381 case STATFLAG_DEFAULT:
382 return STG_E_INSUFFICIENTMEMORY;
384 case STATFLAG_NONAME:
385 pstatstg->cbSize.QuadPart = instance->m_lSize.QuadPart;
386 pstatstg->grfLocksSupported = LOCK_EXCLUSIVE;
387 pstatstg->grfMode = GENERIC_READ;
388 pstatstg->grfStateBits = 0;
389 pstatstg->type = STGTY_STREAM;
392 case STATFLAG_NOOPEN:
393 return STG_E_INVALIDFLAG;
396 return STG_E_INVALIDFLAG;
402static HRESULT STDMETHODCALLTYPE CliprdrStream_Clone(IStream* This, IStream** ppstm)
409static CliprdrStream* CliprdrStream_New(ULONG index,
void* pData,
const FILEDESCRIPTORW* dsc)
412 BOOL success = FALSE;
414 CliprdrStream* instance;
415 wfClipboard* clipboard = (wfClipboard*)pData;
416 instance = (CliprdrStream*)calloc(1,
sizeof(CliprdrStream));
420 instance->m_Dsc = *dsc;
421 iStream = &instance->iStream;
422 iStream->lpVtbl = (IStreamVtbl*)calloc(1,
sizeof(IStreamVtbl));
426 iStream->lpVtbl->QueryInterface = CliprdrStream_QueryInterface;
427 iStream->lpVtbl->AddRef = CliprdrStream_AddRef;
428 iStream->lpVtbl->Release = CliprdrStream_Release;
429 iStream->lpVtbl->Read = CliprdrStream_Read;
430 iStream->lpVtbl->Write = CliprdrStream_Write;
431 iStream->lpVtbl->Seek = CliprdrStream_Seek;
432 iStream->lpVtbl->SetSize = CliprdrStream_SetSize;
433 iStream->lpVtbl->CopyTo = CliprdrStream_CopyTo;
434 iStream->lpVtbl->Commit = CliprdrStream_Commit;
435 iStream->lpVtbl->Revert = CliprdrStream_Revert;
436 iStream->lpVtbl->LockRegion = CliprdrStream_LockRegion;
437 iStream->lpVtbl->UnlockRegion = CliprdrStream_UnlockRegion;
438 iStream->lpVtbl->Stat = CliprdrStream_Stat;
439 iStream->lpVtbl->Clone = CliprdrStream_Clone;
440 instance->m_lRefCount = 1;
441 instance->m_lIndex = index;
442 instance->m_pData = pData;
443 instance->m_lOffset.QuadPart = 0;
445 if (instance->m_Dsc.dwFlags & FD_ATTRIBUTES)
447 if (instance->m_Dsc.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
451 if (((instance->m_Dsc.dwFlags & FD_FILESIZE) == 0) && !isDir)
454 if (cliprdr_send_request_filecontents(clipboard, (
void*)instance,
455 instance->m_lIndex, FILECONTENTS_SIZE, 0,
461 instance->m_lSize.QuadPart = *((LONGLONG*)clipboard->req_fdata);
462 free(clipboard->req_fdata);
466 instance->m_lSize.QuadPart =
467 ((UINT64)instance->m_Dsc.nFileSizeHigh << 32) | instance->m_Dsc.nFileSizeLow;
475 CliprdrStream_Delete(instance);
482void CliprdrStream_Delete(CliprdrStream* instance)
486 free(instance->iStream.lpVtbl);
495static LONG cliprdr_lookup_format(CliprdrDataObject* instance, FORMATETC* pFormatEtc)
497 if (!instance || !pFormatEtc)
500 for (ULONG i = 0; i < instance->m_nNumFormats; i++)
502 if ((pFormatEtc->tymed & instance->m_pFormatEtc[i].tymed) &&
503 pFormatEtc->cfFormat == instance->m_pFormatEtc[i].cfFormat &&
504 pFormatEtc->dwAspect & instance->m_pFormatEtc[i].dwAspect)
513static HRESULT STDMETHODCALLTYPE CliprdrDataObject_QueryInterface(IDataObject* This, REFIID riid,
521 if (IsEqualIID(riid, &IID_IDataObject) || IsEqualIID(riid, &IID_IUnknown))
523 IDataObject_AddRef(This);
530 return E_NOINTERFACE;
534static ULONG STDMETHODCALLTYPE CliprdrDataObject_AddRef(IDataObject* This)
536 CliprdrDataObject* instance = (CliprdrDataObject*)This;
541 return InterlockedIncrement(&instance->m_lRefCount);
544static ULONG STDMETHODCALLTYPE CliprdrDataObject_Release(IDataObject* This)
547 CliprdrDataObject* instance = (CliprdrDataObject*)This;
552 count = InterlockedDecrement(&instance->m_lRefCount);
556 CliprdrDataObject_Delete(instance);
563static HRESULT STDMETHODCALLTYPE CliprdrDataObject_GetData(IDataObject* This, FORMATETC* pFormatEtc,
567 CliprdrDataObject* instance = (CliprdrDataObject*)This;
568 wfClipboard* clipboard;
570 if (!pFormatEtc || !pMedium || !instance)
573 clipboard = (wfClipboard*)instance->m_pData;
578 if ((idx = cliprdr_lookup_format(instance, pFormatEtc)) == -1)
579 return DV_E_FORMATETC;
581 pMedium->tymed = instance->m_pFormatEtc[idx].tymed;
582 pMedium->pUnkForRelease = 0;
584 if (instance->m_pFormatEtc[idx].cfFormat == RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW))
586 FILEGROUPDESCRIPTOR* dsc;
587 DWORD remote = get_remote_format_id(clipboard, instance->m_pFormatEtc[idx].cfFormat);
589 if (cliprdr_send_data_request(clipboard, remote) != 0)
592 pMedium->u.hGlobal = clipboard->hmem;
597 dsc = (FILEGROUPDESCRIPTOR*)GlobalLock(clipboard->hmem);
598 instance->m_nStreams = dsc->cItems;
599 GlobalUnlock(clipboard->hmem);
601 if (instance->m_nStreams > 0)
603 if (!instance->m_pStream)
605 instance->m_pStream = (LPSTREAM*)calloc(instance->m_nStreams,
sizeof(LPSTREAM));
607 if (instance->m_pStream)
609 for (ULONG i = 0; i < instance->m_nStreams; i++)
611 instance->m_pStream[i] =
612 (IStream*)CliprdrStream_New(i, clipboard, &dsc->fgd[i]);
614 if (!instance->m_pStream[i])
615 return E_OUTOFMEMORY;
621 if (!instance->m_pStream)
625 GlobalFree(clipboard->hmem);
626 clipboard->hmem =
nullptr;
629 pMedium->u.hGlobal =
nullptr;
630 return E_OUTOFMEMORY;
633 else if (instance->m_pFormatEtc[idx].cfFormat == RegisterClipboardFormat(CFSTR_FILECONTENTS))
635 if ((pFormatEtc->lindex >= 0) && ((ULONG)pFormatEtc->lindex < instance->m_nStreams))
637 pMedium->u.pstm = instance->m_pStream[pFormatEtc->lindex];
638 IDataObject_AddRef(instance->m_pStream[pFormatEtc->lindex]);
649static HRESULT STDMETHODCALLTYPE CliprdrDataObject_GetDataHere(IDataObject* This,
650 FORMATETC* pformatetc,
659static HRESULT STDMETHODCALLTYPE CliprdrDataObject_QueryGetData(IDataObject* This,
660 FORMATETC* pformatetc)
662 CliprdrDataObject* instance = (CliprdrDataObject*)This;
667 if (cliprdr_lookup_format(instance, pformatetc) == -1)
668 return DV_E_FORMATETC;
673static HRESULT STDMETHODCALLTYPE CliprdrDataObject_GetCanonicalFormatEtc(IDataObject* This,
674 FORMATETC* pformatectIn,
675 FORMATETC* pformatetcOut)
683 pformatetcOut->ptd =
nullptr;
687static HRESULT STDMETHODCALLTYPE CliprdrDataObject_SetData(IDataObject* This, FORMATETC* pformatetc,
688 STGMEDIUM* pmedium, BOOL fRelease)
697static HRESULT STDMETHODCALLTYPE CliprdrDataObject_EnumFormatEtc(IDataObject* This,
699 IEnumFORMATETC** ppenumFormatEtc)
701 CliprdrDataObject* instance = (CliprdrDataObject*)This;
703 if (!instance || !ppenumFormatEtc)
706 if (dwDirection == DATADIR_GET)
708 *ppenumFormatEtc = (IEnumFORMATETC*)CliprdrEnumFORMATETC_New(instance->m_nNumFormats,
709 instance->m_pFormatEtc);
710 return (*ppenumFormatEtc) ? S_OK : E_OUTOFMEMORY;
718static HRESULT STDMETHODCALLTYPE CliprdrDataObject_DAdvise(IDataObject* This, FORMATETC* pformatetc,
719 DWORD advf, IAdviseSink* pAdvSink,
720 DWORD* pdwConnection)
727 return OLE_E_ADVISENOTSUPPORTED;
730static HRESULT STDMETHODCALLTYPE CliprdrDataObject_DUnadvise(IDataObject* This, DWORD dwConnection)
734 return OLE_E_ADVISENOTSUPPORTED;
737static HRESULT STDMETHODCALLTYPE CliprdrDataObject_EnumDAdvise(IDataObject* This,
738 IEnumSTATDATA** ppenumAdvise)
742 return OLE_E_ADVISENOTSUPPORTED;
745static CliprdrDataObject* CliprdrDataObject_New(FORMATETC* fmtetc, STGMEDIUM* stgmed, ULONG count,
748 CliprdrDataObject* instance;
749 IDataObject* iDataObject;
750 instance = (CliprdrDataObject*)calloc(1,
sizeof(CliprdrDataObject));
755 iDataObject = &instance->iDataObject;
756 iDataObject->lpVtbl = (IDataObjectVtbl*)calloc(1,
sizeof(IDataObjectVtbl));
758 if (!iDataObject->lpVtbl)
761 iDataObject->lpVtbl->QueryInterface = CliprdrDataObject_QueryInterface;
762 iDataObject->lpVtbl->AddRef = CliprdrDataObject_AddRef;
763 iDataObject->lpVtbl->Release = CliprdrDataObject_Release;
764 iDataObject->lpVtbl->GetData = CliprdrDataObject_GetData;
765 iDataObject->lpVtbl->GetDataHere = CliprdrDataObject_GetDataHere;
766 iDataObject->lpVtbl->QueryGetData = CliprdrDataObject_QueryGetData;
767 iDataObject->lpVtbl->GetCanonicalFormatEtc = CliprdrDataObject_GetCanonicalFormatEtc;
768 iDataObject->lpVtbl->SetData = CliprdrDataObject_SetData;
769 iDataObject->lpVtbl->EnumFormatEtc = CliprdrDataObject_EnumFormatEtc;
770 iDataObject->lpVtbl->DAdvise = CliprdrDataObject_DAdvise;
771 iDataObject->lpVtbl->DUnadvise = CliprdrDataObject_DUnadvise;
772 iDataObject->lpVtbl->EnumDAdvise = CliprdrDataObject_EnumDAdvise;
773 instance->m_lRefCount = 1;
774 instance->m_nNumFormats = count;
775 instance->m_pData = data;
776 instance->m_nStreams = 0;
777 instance->m_pStream =
nullptr;
781 instance->m_pFormatEtc = (FORMATETC*)calloc(count,
sizeof(FORMATETC));
783 if (!instance->m_pFormatEtc)
786 instance->m_pStgMedium = (STGMEDIUM*)calloc(count,
sizeof(STGMEDIUM));
788 if (!instance->m_pStgMedium)
791 for (ULONG i = 0; i < count; i++)
793 instance->m_pFormatEtc[i] = fmtetc[i];
794 instance->m_pStgMedium[i] = stgmed[i];
800 CliprdrDataObject_Delete(instance);
804void CliprdrDataObject_Delete(CliprdrDataObject* instance)
808 free(instance->iDataObject.lpVtbl);
809 free(instance->m_pFormatEtc);
810 free(instance->m_pStgMedium);
812 if (instance->m_pStream)
814 for (ULONG i = 0; i < instance->m_nStreams; i++)
815 CliprdrStream_Release(instance->m_pStream[i]);
817 free(instance->m_pStream);
824static BOOL wf_create_file_obj(wfClipboard* clipboard, IDataObject** ppDataObject)
826 FORMATETC fmtetc[2] = WINPR_C_ARRAY_INIT;
827 STGMEDIUM stgmeds[2] = WINPR_C_ARRAY_INIT;
832 fmtetc[0].cfFormat = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
833 fmtetc[0].dwAspect = DVASPECT_CONTENT;
835 fmtetc[0].tymed = TYMED_HGLOBAL;
836 stgmeds[0].tymed = TYMED_HGLOBAL;
838 fmtetc[1].cfFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS);
839 fmtetc[1].dwAspect = DVASPECT_CONTENT;
841 fmtetc[1].tymed = TYMED_ISTREAM;
842 stgmeds[1].tymed = TYMED_ISTREAM;
844 *ppDataObject = (IDataObject*)CliprdrDataObject_New(fmtetc, stgmeds, 2, clipboard);
845 return (*ppDataObject) ? TRUE : FALSE;
848static void wf_destroy_file_obj(IDataObject* instance)
851 IDataObject_Release(instance);
858static void cliprdr_format_deep_copy(FORMATETC* dest, FORMATETC* source)
864 dest->ptd = (DVTARGETDEVICE*)CoTaskMemAlloc(
sizeof(DVTARGETDEVICE));
867 *(dest->ptd) = *(source->ptd);
871static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_QueryInterface(IEnumFORMATETC* This,
872 REFIID riid,
void** ppvObject)
876 if (IsEqualIID(riid, &IID_IEnumFORMATETC) || IsEqualIID(riid, &IID_IUnknown))
878 IEnumFORMATETC_AddRef(This);
885 return E_NOINTERFACE;
889static ULONG STDMETHODCALLTYPE CliprdrEnumFORMATETC_AddRef(IEnumFORMATETC* This)
891 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
896 return InterlockedIncrement(&instance->m_lRefCount);
899static ULONG STDMETHODCALLTYPE CliprdrEnumFORMATETC_Release(IEnumFORMATETC* This)
902 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
907 count = InterlockedDecrement(&instance->m_lRefCount);
911 CliprdrEnumFORMATETC_Delete(instance);
920static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_Next(IEnumFORMATETC* This, ULONG celt,
921 FORMATETC* rgelt, ULONG* pceltFetched)
924 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
926 if (!instance || !celt || !rgelt)
929 while ((instance->m_nIndex < instance->m_nNumFormats) && (copied < celt))
931 cliprdr_format_deep_copy(&rgelt[copied++], &instance->m_pFormatEtc[instance->m_nIndex++]);
934 if (pceltFetched != 0)
935 *pceltFetched = copied;
937 return (copied == celt) ? S_OK : E_FAIL;
940static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_Skip(IEnumFORMATETC* This, ULONG celt)
942 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
947 if (instance->m_nIndex + (LONG)celt > instance->m_nNumFormats)
950 instance->m_nIndex += celt;
954static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_Reset(IEnumFORMATETC* This)
956 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
961 instance->m_nIndex = 0;
965static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_Clone(IEnumFORMATETC* This,
966 IEnumFORMATETC** ppEnum)
968 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
970 if (!instance || !ppEnum)
974 (IEnumFORMATETC*)CliprdrEnumFORMATETC_New(instance->m_nNumFormats, instance->m_pFormatEtc);
977 return E_OUTOFMEMORY;
979 ((CliprdrEnumFORMATETC*)*ppEnum)->m_nIndex = instance->m_nIndex;
983CliprdrEnumFORMATETC* CliprdrEnumFORMATETC_New(ULONG nFormats, FORMATETC* pFormatEtc)
985 CliprdrEnumFORMATETC* instance;
986 IEnumFORMATETC* iEnumFORMATETC;
988 if ((nFormats != 0) && !pFormatEtc)
991 instance = (CliprdrEnumFORMATETC*)calloc(1,
sizeof(CliprdrEnumFORMATETC));
996 iEnumFORMATETC = &instance->iEnumFORMATETC;
997 iEnumFORMATETC->lpVtbl = (IEnumFORMATETCVtbl*)calloc(1,
sizeof(IEnumFORMATETCVtbl));
999 if (!iEnumFORMATETC->lpVtbl)
1002 iEnumFORMATETC->lpVtbl->QueryInterface = CliprdrEnumFORMATETC_QueryInterface;
1003 iEnumFORMATETC->lpVtbl->AddRef = CliprdrEnumFORMATETC_AddRef;
1004 iEnumFORMATETC->lpVtbl->Release = CliprdrEnumFORMATETC_Release;
1005 iEnumFORMATETC->lpVtbl->Next = CliprdrEnumFORMATETC_Next;
1006 iEnumFORMATETC->lpVtbl->Skip = CliprdrEnumFORMATETC_Skip;
1007 iEnumFORMATETC->lpVtbl->Reset = CliprdrEnumFORMATETC_Reset;
1008 iEnumFORMATETC->lpVtbl->Clone = CliprdrEnumFORMATETC_Clone;
1009 instance->m_lRefCount = 1;
1010 instance->m_nIndex = 0;
1011 instance->m_nNumFormats = nFormats;
1015 instance->m_pFormatEtc = (FORMATETC*)calloc(nFormats,
sizeof(FORMATETC));
1017 if (!instance->m_pFormatEtc)
1020 for (ULONG i = 0; i < nFormats; i++)
1021 cliprdr_format_deep_copy(&instance->m_pFormatEtc[i], &pFormatEtc[i]);
1026 CliprdrEnumFORMATETC_Delete(instance);
1030void CliprdrEnumFORMATETC_Delete(CliprdrEnumFORMATETC* instance)
1034 free(instance->iEnumFORMATETC.lpVtbl);
1036 if (instance->m_pFormatEtc)
1038 for (LONG i = 0; i < instance->m_nNumFormats; i++)
1040 if (instance->m_pFormatEtc[i].ptd)
1041 CoTaskMemFree(instance->m_pFormatEtc[i].ptd);
1044 free(instance->m_pFormatEtc);
1053static UINT32 get_local_format_id_by_name(wfClipboard* clipboard,
const TCHAR* format_name)
1056 WCHAR* unicode_name;
1057#if !defined(UNICODE)
1061 if (!clipboard || !format_name)
1065 unicode_name = _wcsdup(format_name);
1067 size = _tcslen(format_name);
1068 unicode_name = calloc(size + 1,
sizeof(WCHAR));
1073 MultiByteToWideChar(CP_OEMCP, 0, format_name, strlen(format_name), unicode_name, size);
1079 for (
size_t i = 0; i < clipboard->map_size; i++)
1081 map = &clipboard->format_mappings[i];
1085 if (wcscmp(map->name, unicode_name) == 0)
1088 return map->local_format_id;
1097static inline BOOL file_transferring(wfClipboard* clipboard)
1099 return get_local_format_id_by_name(clipboard, CFSTR_FILEDESCRIPTORW) ? TRUE : FALSE;
1102static UINT32 get_remote_format_id(wfClipboard* clipboard, UINT32 local_format)
1109 for (UINT32 i = 0; i < clipboard->map_size; i++)
1111 map = &clipboard->format_mappings[i];
1113 if (map->local_format_id == local_format)
1114 return map->remote_format_id;
1117 return local_format;
1120static void map_ensure_capacity(wfClipboard* clipboard)
1125 if (clipboard->map_size >= clipboard->map_capacity)
1127 size_t new_size = clipboard->map_capacity;
1130 WINPR_ASSERT(new_size <= SIZE_MAX - 128ull);
1132 }
while (new_size <= clipboard->map_size);
1133 formatMapping* new_map =
1134 (formatMapping*)realloc(clipboard->format_mappings,
sizeof(formatMapping) * new_size);
1139 clipboard->format_mappings = new_map;
1140 clipboard->map_capacity = new_size;
1144static BOOL clear_format_map(wfClipboard* clipboard)
1151 if (clipboard->format_mappings)
1153 for (
size_t i = 0; i < clipboard->map_capacity; i++)
1155 map = &clipboard->format_mappings[i];
1156 map->remote_format_id = 0;
1157 map->local_format_id = 0;
1159 map->name =
nullptr;
1163 clipboard->map_size = 0;
1167static UINT cliprdr_send_tempdir(wfClipboard* clipboard)
1174 if (GetEnvironmentVariableA(
"TEMP", tempDirectory.szTempDir,
sizeof(tempDirectory.szTempDir)) ==
1178 return clipboard->context->TempDirectory(clipboard->context, &tempDirectory);
1181static BOOL cliprdr_GetUpdatedClipboardFormats(wfClipboard* clipboard, PUINT lpuiFormats,
1182 UINT cFormats, PUINT pcFormatsOut)
1186 BOOL clipboardOpen = FALSE;
1188 if (!clipboard->legacyApi)
1189 return clipboard->GetUpdatedClipboardFormats(lpuiFormats, cFormats, pcFormatsOut);
1191 clipboardOpen = try_open_clipboard(clipboard->hwnd);
1199 while (index < cFormats)
1201 format = EnumClipboardFormats(format);
1206 lpuiFormats[index] = format;
1210 *pcFormatsOut = index;
1215static UINT cliprdr_send_format_list(wfClipboard* clipboard)
1219 UINT32 numFormats = 0;
1220 UINT32 formatId = 0;
1221 char formatName[1024];
1226 return ERROR_INTERNAL_ERROR;
1229 if (try_open_clipboard(clipboard->hwnd))
1231 count = CountClipboardFormats();
1232 numFormats = (UINT32)count;
1238 return CHANNEL_RC_NO_MEMORY;
1244 if (IsClipboardFormatAvailable(CF_HDROP))
1246 formats[index++].formatId = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
1247 formats[index++].formatId = RegisterClipboardFormat(CFSTR_FILECONTENTS);
1251 while ((formatId = EnumClipboardFormats(formatId)) != 0)
1252 formats[index++].formatId = formatId;
1258 if (!CloseClipboard())
1261 return ERROR_INTERNAL_ERROR;
1264 for (UINT index = 0; index < numFormats; index++)
1266 if (GetClipboardFormatNameA(formats[index].formatId, formatName,
sizeof(formatName)))
1268 formats[index].formatName = _strdup(formatName);
1273 formatList.numFormats = numFormats;
1274 formatList.formats = formats;
1275 formatList.common.msgType = CB_FORMAT_LIST;
1276 rc = clipboard->context->ClientFormatList(clipboard->context, &formatList);
1278 for (UINT index = 0; index < numFormats; index++)
1279 free(formats[index].formatName);
1285static UINT cliprdr_send_data_request(wfClipboard* clipboard, UINT32 formatId)
1288 UINT32 remoteFormatId;
1291 if (!clipboard || !clipboard->context || !clipboard->context->ClientFormatDataRequest)
1292 return ERROR_INTERNAL_ERROR;
1294 remoteFormatId = get_remote_format_id(clipboard, formatId);
1296 formatDataRequest.requestedFormatId = remoteFormatId;
1297 clipboard->requestedFormatId = formatId;
1298 rc = clipboard->context->ClientFormatDataRequest(clipboard->context, &formatDataRequest);
1300 if (WaitForSingleObject(clipboard->response_data_event, INFINITE) != WAIT_OBJECT_0)
1301 rc = ERROR_INTERNAL_ERROR;
1302 else if (!ResetEvent(clipboard->response_data_event))
1303 rc = ERROR_INTERNAL_ERROR;
1308UINT cliprdr_send_request_filecontents(wfClipboard* clipboard,
const void* streamid, ULONG index,
1309 UINT32 flag, UINT64 position, ULONG nreq)
1314 if (!clipboard || !clipboard->context || !clipboard->context->ClientFileContentsRequest)
1315 return ERROR_INTERNAL_ERROR;
1317 fileContentsRequest.streamId = (UINT32)(ULONG_PTR)streamid;
1318 fileContentsRequest.listIndex = index;
1319 fileContentsRequest.dwFlags = flag;
1320 fileContentsRequest.nPositionLow = position & 0xFFFFFFFF;
1321 fileContentsRequest.nPositionHigh = (position >> 32) & 0xFFFFFFFF;
1322 fileContentsRequest.cbRequested = nreq;
1323 fileContentsRequest.clipDataId = 0;
1324 fileContentsRequest.common.msgFlags = 0;
1325 rc = clipboard->context->ClientFileContentsRequest(clipboard->context, &fileContentsRequest);
1327 if (WaitForSingleObject(clipboard->req_fevent, INFINITE) != WAIT_OBJECT_0)
1328 rc = ERROR_INTERNAL_ERROR;
1329 else if (!ResetEvent(clipboard->req_fevent))
1330 rc = ERROR_INTERNAL_ERROR;
1332 if (nreq != clipboard->req_fsize)
1333 rc = ERROR_INVALID_DATA;
1337static UINT cliprdr_send_response_filecontents(wfClipboard* clipboard, UINT32 streamId, UINT32 size,
1342 if (!clipboard || !clipboard->context || !clipboard->context->ClientFileContentsResponse)
1343 return ERROR_INTERNAL_ERROR;
1345 fileContentsResponse.streamId = streamId;
1346 fileContentsResponse.cbRequested = size;
1347 fileContentsResponse.requestedData = data;
1348 fileContentsResponse.common.msgFlags = CB_RESPONSE_OK;
1349 return clipboard->context->ClientFileContentsResponse(clipboard->context,
1350 &fileContentsResponse);
1353static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
1355 static wfClipboard* clipboard =
nullptr;
1360 DEBUG_CLIPRDR(
"info: WM_CREATE");
1361 clipboard = (wfClipboard*)((CREATESTRUCT*)lParam)->lpCreateParams;
1362 clipboard->hwnd = hWnd;
1364 if (!clipboard->legacyApi)
1365 clipboard->AddClipboardFormatListener(hWnd);
1367 clipboard->hWndNextViewer = SetClipboardViewer(hWnd);
1372 DEBUG_CLIPRDR(
"info: WM_CLOSE");
1374 if (!clipboard->legacyApi)
1375 clipboard->RemoveClipboardFormatListener(hWnd);
1380 if (clipboard->legacyApi)
1381 ChangeClipboardChain(hWnd, clipboard->hWndNextViewer);
1385 case WM_CLIPBOARDUPDATE:
1386 DEBUG_CLIPRDR(
"info: WM_CLIPBOARDUPDATE");
1388 if (clipboard->sync)
1390 if ((GetClipboardOwner() != clipboard->hwnd) &&
1391 (S_FALSE == OleIsCurrentClipboard(clipboard->data_obj)))
1393 if (clipboard->hmem)
1395 GlobalFree(clipboard->hmem);
1396 clipboard->hmem =
nullptr;
1399 cliprdr_send_format_list(clipboard);
1405 case WM_RENDERALLFORMATS:
1406 DEBUG_CLIPRDR(
"info: WM_RENDERALLFORMATS");
1409 if (!try_open_clipboard(clipboard->hwnd))
1411 DEBUG_CLIPRDR(
"OpenClipboard failed with 0x%x", GetLastError());
1419 case WM_RENDERFORMAT:
1420 DEBUG_CLIPRDR(
"info: WM_RENDERFORMAT");
1422 if (cliprdr_send_data_request(clipboard, (UINT32)wParam) != 0)
1424 DEBUG_CLIPRDR(
"error: cliprdr_send_data_request failed.");
1428 if (!SetClipboardData((UINT)wParam, clipboard->hmem))
1430 DEBUG_CLIPRDR(
"SetClipboardData failed with 0x%x", GetLastError());
1432 if (clipboard->hmem)
1434 GlobalFree(clipboard->hmem);
1435 clipboard->hmem =
nullptr;
1442 case WM_DRAWCLIPBOARD:
1443 if (clipboard->legacyApi)
1445 if ((GetClipboardOwner() != clipboard->hwnd) &&
1446 (S_FALSE == OleIsCurrentClipboard(clipboard->data_obj)))
1448 cliprdr_send_format_list(clipboard);
1451 SendMessage(clipboard->hWndNextViewer, Msg, wParam, lParam);
1456 case WM_CHANGECBCHAIN:
1457 if (clipboard->legacyApi)
1459 HWND hWndCurrViewer = (HWND)wParam;
1460 HWND hWndNextViewer = (HWND)lParam;
1462 if (hWndCurrViewer == clipboard->hWndNextViewer)
1463 clipboard->hWndNextViewer = hWndNextViewer;
1464 else if (clipboard->hWndNextViewer)
1465 SendMessage(clipboard->hWndNextViewer, Msg, wParam, lParam);
1470 case WM_CLIPRDR_MESSAGE:
1471 DEBUG_CLIPRDR(
"info: WM_CLIPRDR_MESSAGE");
1475 case OLE_SETCLIPBOARD:
1476 DEBUG_CLIPRDR(
"info: OLE_SETCLIPBOARD");
1478 if (wf_create_file_obj(clipboard, &clipboard->data_obj))
1480 if (OleSetClipboard(clipboard->data_obj) != S_OK)
1482 wf_destroy_file_obj(clipboard->data_obj);
1483 clipboard->data_obj =
nullptr;
1495 case WM_DESTROYCLIPBOARD:
1496 case WM_ASKCBFORMATNAME:
1497 case WM_HSCROLLCLIPBOARD:
1498 case WM_PAINTCLIPBOARD:
1499 case WM_SIZECLIPBOARD:
1500 case WM_VSCROLLCLIPBOARD:
1502 return DefWindowProc(hWnd, Msg, wParam, lParam);
1508static int create_cliprdr_window(wfClipboard* clipboard)
1510 WNDCLASSEX wnd_cls = WINPR_C_ARRAY_INIT;
1512 wnd_cls.cbSize =
sizeof(WNDCLASSEX);
1513 wnd_cls.style = CS_OWNDC;
1514 wnd_cls.lpfnWndProc = cliprdr_proc;
1515 wnd_cls.cbClsExtra = 0;
1516 wnd_cls.cbWndExtra = 0;
1517 wnd_cls.hIcon =
nullptr;
1518 wnd_cls.hCursor =
nullptr;
1519 wnd_cls.hbrBackground =
nullptr;
1520 wnd_cls.lpszMenuName =
nullptr;
1521 wnd_cls.lpszClassName = _T(
"ClipboardHiddenMessageProcessor");
1522 wnd_cls.hInstance = GetModuleHandle(
nullptr);
1523 wnd_cls.hIconSm =
nullptr;
1524 RegisterClassEx(&wnd_cls);
1526 CreateWindowEx(WS_EX_LEFT, _T(
"ClipboardHiddenMessageProcessor"), _T(
"rdpclip"), 0, 0, 0, 0,
1527 0, HWND_MESSAGE,
nullptr, GetModuleHandle(
nullptr), clipboard);
1529 if (!clipboard->hwnd)
1531 DEBUG_CLIPRDR(
"error: CreateWindowEx failed with %x.", GetLastError());
1538static DWORD WINAPI cliprdr_thread_func(LPVOID arg)
1543 wfClipboard* clipboard = (wfClipboard*)arg;
1546 if ((ret = create_cliprdr_window(clipboard)) != 0)
1549 DEBUG_CLIPRDR(
"error: create clipboard window failed.");
1553 while ((mcode = GetMessage(&msg, 0, 0, 0)) != 0)
1557 DEBUG_CLIPRDR(
"error: clipboard thread GetMessage failed.");
1562 TranslateMessage(&msg);
1563 DispatchMessage(&msg);
1571static void clear_file_array(wfClipboard* clipboard)
1577 if (clipboard->file_names)
1579 for (
size_t i = 0; i < clipboard->nFiles; i++)
1581 free(clipboard->file_names[i]);
1582 clipboard->file_names[i] =
nullptr;
1585 free(clipboard->file_names);
1586 clipboard->file_names =
nullptr;
1590 if (clipboard->fileDescriptor)
1592 for (
size_t i = 0; i < clipboard->nFiles; i++)
1594 free(clipboard->fileDescriptor[i]);
1595 clipboard->fileDescriptor[i] =
nullptr;
1598 free(clipboard->fileDescriptor);
1599 clipboard->fileDescriptor =
nullptr;
1602 clipboard->file_array_size = 0;
1603 clipboard->nFiles = 0;
1606static BOOL wf_cliprdr_get_file_contents(WCHAR* file_name, BYTE* buffer, LONG positionLow,
1607 LONG positionHigh, DWORD nRequested, DWORD* puSize)
1613 if (!file_name || !buffer || !puSize)
1615 WLog_ERR(TAG,
"get file contents Invalid Arguments.");
1619 hFile = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING,
1620 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
nullptr);
1622 if (hFile == INVALID_HANDLE_VALUE)
1625 rc = SetFilePointer(hFile, positionLow, &positionHigh, FILE_BEGIN);
1627 if (rc == INVALID_SET_FILE_POINTER)
1630 if (!ReadFile(hFile, buffer, nRequested, &nGet,
nullptr))
1632 DEBUG_CLIPRDR(
"ReadFile failed with 0x%08lX.", GetLastError());
1639 if (!CloseHandle(hFile))
1649static FILEDESCRIPTORW* wf_cliprdr_get_file_descriptor(WCHAR* file_name,
size_t pathLen)
1658 hFile = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING,
1659 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
nullptr);
1661 if (hFile == INVALID_HANDLE_VALUE)
1667 fd->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI;
1668 fd->dwFileAttributes = GetFileAttributes(file_name);
1670 if (!GetFileTime(hFile,
nullptr,
nullptr, &fd->ftLastWriteTime))
1672 fd->dwFlags &= ~FD_WRITESTIME;
1675 fd->nFileSizeLow = GetFileSize(hFile, &fd->nFileSizeHigh);
1676 wcscpy_s(fd->cFileName,
sizeof(fd->cFileName) / 2, file_name + pathLen);
1677 (void)CloseHandle(hFile);
1681static BOOL wf_cliprdr_array_ensure_capacity(wfClipboard* clipboard)
1686 if (clipboard->nFiles == clipboard->file_array_size)
1691 new_size = (clipboard->file_array_size + 1) * 2;
1696 clipboard->fileDescriptor = new_fd;
1698 new_name = (WCHAR**)realloc(clipboard->file_names, new_size *
sizeof(WCHAR*));
1701 clipboard->file_names = new_name;
1703 if (!new_fd || !new_name)
1706 clipboard->file_array_size = new_size;
1712static BOOL wf_cliprdr_add_to_file_arrays(wfClipboard* clipboard, WCHAR* full_file_name,
1715 if (!wf_cliprdr_array_ensure_capacity(clipboard))
1719 clipboard->file_names[clipboard->nFiles] = (LPWSTR)malloc(MAX_PATH * 2);
1721 if (!clipboard->file_names[clipboard->nFiles])
1724 wcscpy_s(clipboard->file_names[clipboard->nFiles], MAX_PATH, full_file_name);
1726 clipboard->fileDescriptor[clipboard->nFiles] =
1727 wf_cliprdr_get_file_descriptor(full_file_name, pathLen);
1729 if (!clipboard->fileDescriptor[clipboard->nFiles])
1731 free(clipboard->file_names[clipboard->nFiles]);
1735 clipboard->nFiles++;
1739static BOOL wf_cliprdr_traverse_directory(wfClipboard* clipboard, WCHAR* Dir,
size_t pathLen)
1742 WCHAR DirSpec[MAX_PATH];
1743 WIN32_FIND_DATA FindFileData;
1745 if (!clipboard || !Dir)
1748 StringCchCopy(DirSpec, MAX_PATH, Dir);
1749 StringCchCat(DirSpec, MAX_PATH, TEXT(
"\\*"));
1750 hFind = FindFirstFile(DirSpec, &FindFileData);
1752 if (hFind == INVALID_HANDLE_VALUE)
1754 DEBUG_CLIPRDR(
"FindFirstFile failed with 0x%x.", GetLastError());
1758 while (FindNextFile(hFind, &FindFileData))
1760 if ((((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) &&
1761 (wcscmp(FindFileData.cFileName, _T(
".")) == 0)) ||
1762 (wcscmp(FindFileData.cFileName, _T(
"..")) == 0))
1767 if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
1769 WCHAR DirAdd[MAX_PATH];
1770 StringCchCopy(DirAdd, MAX_PATH, Dir);
1771 StringCchCat(DirAdd, MAX_PATH, _T(
"\\"));
1772 StringCchCat(DirAdd, MAX_PATH, FindFileData.cFileName);
1774 if (!wf_cliprdr_add_to_file_arrays(clipboard, DirAdd, pathLen))
1777 if (!wf_cliprdr_traverse_directory(clipboard, DirAdd, pathLen))
1782 WCHAR fileName[MAX_PATH];
1783 StringCchCopy(fileName, MAX_PATH, Dir);
1784 StringCchCat(fileName, MAX_PATH, _T(
"\\"));
1785 StringCchCat(fileName, MAX_PATH, FindFileData.cFileName);
1787 if (!wf_cliprdr_add_to_file_arrays(clipboard, fileName, pathLen))
1796static UINT wf_cliprdr_send_client_capabilities(wfClipboard* clipboard)
1801 if (!clipboard || !clipboard->context || !clipboard->context->ClientCapabilities)
1802 return ERROR_INTERNAL_ERROR;
1804 capabilities.cCapabilitiesSets = 1;
1806 generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
1807 generalCapabilitySet.capabilitySetLength = 12;
1808 generalCapabilitySet.version = CB_CAPS_VERSION_2;
1809 generalCapabilitySet.generalFlags =
1810 CB_USE_LONG_FORMAT_NAMES | CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS;
1811 return clipboard->context->ClientCapabilities(clipboard->context, &capabilities);
1819static UINT wf_cliprdr_monitor_ready(CliprdrClientContext* context,
1823 wfClipboard* clipboard = (wfClipboard*)context->custom;
1825 if (!context || !monitorReady)
1826 return ERROR_INTERNAL_ERROR;
1828 clipboard->sync = TRUE;
1829 rc = wf_cliprdr_send_client_capabilities(clipboard);
1831 if (rc != CHANNEL_RC_OK)
1834 return cliprdr_send_format_list(clipboard);
1842static UINT wf_cliprdr_server_capabilities(CliprdrClientContext* context,
1846 wfClipboard* clipboard = (wfClipboard*)context->custom;
1848 if (!context || !capabilities)
1849 return ERROR_INTERNAL_ERROR;
1851 for (UINT32 index = 0; index < capabilities->cCapabilitiesSets; index++)
1853 capabilitySet = &(capabilities->capabilitySets[index]);
1855 if ((capabilitySet->capabilitySetType == CB_CAPSTYPE_GENERAL) &&
1856 (capabilitySet->capabilitySetLength >= CB_CAPSTYPE_GENERAL_LEN))
1860 clipboard->capabilities = generalCapabilitySet->generalFlags;
1865 return CHANNEL_RC_OK;
1873static UINT wf_cliprdr_server_format_list(CliprdrClientContext* context,
1876 UINT rc = ERROR_INTERNAL_ERROR;
1877 formatMapping* mapping;
1879 wfClipboard* clipboard = (wfClipboard*)context->custom;
1881 if (!clear_format_map(clipboard))
1882 return ERROR_INTERNAL_ERROR;
1884 for (UINT32 i = 0; i < formatList->numFormats; i++)
1886 format = &(formatList->formats[i]);
1887 mapping = &(clipboard->format_mappings[i]);
1888 mapping->remote_format_id = format->formatId;
1890 if (format->formatName)
1892 mapping->name = ConvertUtf8ToWCharAlloc(format->formatName,
nullptr);
1895 mapping->local_format_id = RegisterClipboardFormatW((LPWSTR)mapping->name);
1899 mapping->name =
nullptr;
1900 mapping->local_format_id = mapping->remote_format_id;
1903 clipboard->map_size++;
1904 map_ensure_capacity(clipboard);
1907 if (file_transferring(clipboard))
1909 if (PostMessage(clipboard->hwnd, WM_CLIPRDR_MESSAGE, OLE_SETCLIPBOARD, 0))
1914 if (!try_open_clipboard(clipboard->hwnd))
1915 return CHANNEL_RC_OK;
1917 if (EmptyClipboard())
1919 for (UINT32 i = 0; i < (UINT32)clipboard->map_size; i++)
1920 SetClipboardData(clipboard->format_mappings[i].local_format_id,
nullptr);
1925 if (!CloseClipboard() && GetLastError())
1926 return ERROR_INTERNAL_ERROR;
1938wf_cliprdr_server_format_list_response(CliprdrClientContext* context,
1942 (void)formatListResponse;
1944 if (formatListResponse->common.msgFlags != CB_RESPONSE_OK)
1945 WLog_WARN(TAG,
"format list update failed");
1947 return CHANNEL_RC_OK;
1956wf_cliprdr_server_lock_clipboard_data(CliprdrClientContext* context,
1960 (void)lockClipboardData;
1961 return CHANNEL_RC_OK;
1970wf_cliprdr_server_unlock_clipboard_data(CliprdrClientContext* context,
1974 (void)unlockClipboardData;
1975 return CHANNEL_RC_OK;
1978static BOOL wf_cliprdr_process_filename(wfClipboard* clipboard, WCHAR* wFileName,
size_t str_len)
1981 size_t offset = str_len;
1983 if (!clipboard || !wFileName)
1989 if (wFileName[offset] == L
'\\')
1995 pathLen = offset + 1;
1997 if (!wf_cliprdr_add_to_file_arrays(clipboard, wFileName, pathLen))
2000 if ((clipboard->fileDescriptor[clipboard->nFiles - 1]->dwFileAttributes &
2001 FILE_ATTRIBUTE_DIRECTORY) != 0)
2004 if (!wf_cliprdr_traverse_directory(clipboard, wFileName, pathLen))
2011static SSIZE_T wf_cliprdr_tryopen(wfClipboard* clipboard, UINT32 requestedFormatId, BYTE** pData)
2014 WINPR_ASSERT(clipboard);
2015 WINPR_ASSERT(pData);
2020 if (!try_open_clipboard(clipboard->hwnd))
2023 HANDLE hClipdata = GetClipboardData(requestedFormatId);
2028 char* globlemem = (
char*)GlobalLock(hClipdata);
2029 const SSIZE_T size = GlobalSize(hClipdata);
2033 BYTE* buff = malloc(size);
2034 if (buff ==
nullptr)
2036 CopyMemory(buff, globlemem, size);
2041 GlobalUnlock(hClipdata);
2049static SSIZE_T wf_cliprdr_get_filedescriptor(wfClipboard* clipboard, BYTE** pData)
2051 WINPR_ASSERT(clipboard);
2052 WINPR_ASSERT(pData);
2055 LPDATAOBJECT dataObj =
nullptr;
2056 FORMATETC format_etc = WINPR_C_ARRAY_INIT;
2057 STGMEDIUM stg_medium = WINPR_C_ARRAY_INIT;
2061 HRESULT result = OleGetClipboard(&dataObj);
2066 format_etc.cfFormat = CF_HDROP;
2067 format_etc.tymed = TYMED_HGLOBAL;
2068 format_etc.dwAspect = 1;
2069 format_etc.lindex = -1;
2070 result = IDataObject_GetData(dataObj, &format_etc, &stg_medium);
2074 DEBUG_CLIPRDR(
"dataObj->GetData failed.");
2078 HGLOBAL hdl = stg_medium.u.hGlobal;
2079 DROPFILES* dropFiles = (DROPFILES*)GlobalLock(hdl);
2083 ReleaseStgMedium(&stg_medium);
2084 clipboard->nFiles = 0;
2088 clear_file_array(clipboard);
2090 if (dropFiles->fWide)
2094 for (WCHAR* wFileName = (WCHAR*)((
char*)dropFiles + dropFiles->pFiles);
2095 (len = wcslen(wFileName)) > 0; wFileName += len + 1)
2097 wf_cliprdr_process_filename(clipboard, wFileName, wcslen(wFileName));
2103 for (
char* p = (
char*)((
char*)dropFiles + dropFiles->pFiles); (len = strlen(p)) > 0;
2104 p += len + 1, clipboard->nFiles++)
2106 const int ilen = WINPR_ASSERTING_INT_CAST(
int, len);
2107 const int cchWideChar = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, ilen,
nullptr, 0);
2108 WCHAR* wFileName = (LPWSTR)calloc(cchWideChar,
sizeof(WCHAR));
2109 MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, ilen, wFileName, cchWideChar);
2110 wf_cliprdr_process_filename(clipboard, wFileName, cchWideChar);
2116 ReleaseStgMedium(&stg_medium);
2119 const size_t size = 4ull + clipboard->nFiles *
sizeof(
FILEDESCRIPTORW);
2120 FILEGROUPDESCRIPTORW* groupDsc = (FILEGROUPDESCRIPTORW*)calloc(size, 1);
2124 groupDsc->cItems = WINPR_ASSERTING_INT_CAST(UINT, clipboard->nFiles);
2126 for (
size_t i = 0; i < clipboard->nFiles; i++)
2128 if (clipboard->fileDescriptor[i])
2129 groupDsc->fgd[i] = *clipboard->fileDescriptor[i];
2132 *pData = (BYTE*)groupDsc;
2137 IDataObject_Release(dataObj);
2147wf_cliprdr_server_format_data_request(CliprdrClientContext* context,
2150 if (!context || !formatDataRequest)
2151 return ERROR_INTERNAL_ERROR;
2153 wfClipboard* clipboard = (wfClipboard*)context->custom;
2156 return ERROR_INTERNAL_ERROR;
2158 const UINT32 requestedFormatId = formatDataRequest->requestedFormatId;
2159 BYTE* requestedFormatData =
nullptr;
2161 if (requestedFormatId == RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW))
2163 res = wf_cliprdr_get_filedescriptor(clipboard, &requestedFormatData);
2167 res = wf_cliprdr_tryopen(clipboard, requestedFormatId, &requestedFormatData);
2170 UINT rc = ERROR_INTERNAL_ERROR;
2174 .common = { .msgType = CB_FORMAT_DATA_RESPONSE,
2175 .msgFlags = CB_RESPONSE_OK,
2176 .dataLen = (uint32_t)res },
2177 .requestedFormatData = requestedFormatData
2180 rc = clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
2185 CB_FORMAT_DATA_RESPONSE,
2186 .msgFlags = CB_RESPONSE_FAIL,
2188 .requestedFormatData =
nullptr };
2190 rc = clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
2193 free(requestedFormatData);
2203wf_cliprdr_server_format_data_response(CliprdrClientContext* context,
2208 wfClipboard* clipboard;
2210 if (!context || !formatDataResponse)
2211 return ERROR_INTERNAL_ERROR;
2213 clipboard = (wfClipboard*)context->custom;
2216 return ERROR_INTERNAL_ERROR;
2218 if (formatDataResponse->common.msgFlags != CB_RESPONSE_OK)
2220 clipboard->hmem =
nullptr;
2222 if (!SetEvent(clipboard->response_data_event))
2223 return ERROR_INTERNAL_ERROR;
2225 return CHANNEL_RC_OK;
2228 hMem = GlobalAlloc(GMEM_MOVEABLE, formatDataResponse->common.dataLen);
2231 return ERROR_INTERNAL_ERROR;
2233 data = (BYTE*)GlobalLock(hMem);
2238 return ERROR_INTERNAL_ERROR;
2241 CopyMemory(data, formatDataResponse->requestedFormatData, formatDataResponse->common.dataLen);
2243 if (!GlobalUnlock(hMem) && GetLastError())
2246 return ERROR_INTERNAL_ERROR;
2249 clipboard->hmem = hMem;
2251 if (!SetEvent(clipboard->response_data_event))
2252 return ERROR_INTERNAL_ERROR;
2254 return CHANNEL_RC_OK;
2263wf_cliprdr_server_file_contents_request(CliprdrClientContext* context,
2267 BYTE* pData =
nullptr;
2268 HRESULT hRet = S_OK;
2269 FORMATETC vFormatEtc = WINPR_C_ARRAY_INIT;
2270 LPDATAOBJECT pDataObj =
nullptr;
2271 STGMEDIUM vStgMedium = WINPR_C_ARRAY_INIT;
2272 BOOL bIsStreamFile = TRUE;
2273 static LPSTREAM pStreamStc =
nullptr;
2274 static UINT32 uStreamIdStc = 0;
2275 wfClipboard* clipboard;
2276 UINT rc = ERROR_INTERNAL_ERROR;
2280 if (!context || !fileContentsRequest)
2281 return ERROR_INTERNAL_ERROR;
2283 clipboard = (wfClipboard*)context->custom;
2286 return ERROR_INTERNAL_ERROR;
2288 cbRequested = fileContentsRequest->cbRequested;
2289 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2290 cbRequested =
sizeof(UINT64);
2292 pData = (BYTE*)calloc(1, cbRequested);
2297 hRet = OleGetClipboard(&pDataObj);
2301 WLog_ERR(TAG,
"filecontents: get ole clipboard failed.");
2305 vFormatEtc.cfFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS);
2306 vFormatEtc.tymed = TYMED_ISTREAM;
2307 vFormatEtc.dwAspect = 1;
2308 vFormatEtc.lindex = fileContentsRequest->listIndex;
2309 vFormatEtc.ptd =
nullptr;
2311 if ((uStreamIdStc != fileContentsRequest->streamId) || !pStreamStc)
2313 LPENUMFORMATETC pEnumFormatEtc;
2315 FORMATETC vFormatEtc2;
2319 IStream_Release(pStreamStc);
2320 pStreamStc =
nullptr;
2323 bIsStreamFile = FALSE;
2324 hRet = IDataObject_EnumFormatEtc(pDataObj, DATADIR_GET, &pEnumFormatEtc);
2330 hRet = IEnumFORMATETC_Next(pEnumFormatEtc, 1, &vFormatEtc2, &CeltFetched);
2334 if (vFormatEtc2.cfFormat == RegisterClipboardFormat(CFSTR_FILECONTENTS))
2336 hRet = IDataObject_GetData(pDataObj, &vFormatEtc, &vStgMedium);
2340 pStreamStc = vStgMedium.u.pstm;
2341 uStreamIdStc = fileContentsRequest->streamId;
2342 bIsStreamFile = TRUE;
2348 }
while (hRet == S_OK);
2352 if (bIsStreamFile == TRUE)
2354 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2356 STATSTG vStatStg = WINPR_C_ARRAY_INIT;
2357 hRet = IStream_Stat(pStreamStc, &vStatStg, STATFLAG_NONAME);
2361 *((UINT32*)&pData[0]) = vStatStg.cbSize.QuadPart & 0xFFFFFFFF;
2362 *((UINT32*)&pData[4]) = (vStatStg.cbSize.QuadPart >> 32) & 0xFFFFFFFF;
2363 uSize = cbRequested;
2366 else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE)
2370 dlibMove.QuadPart = (INT64)(((UINT64)fileContentsRequest->nPositionHigh << 32) |
2371 fileContentsRequest->nPositionLow);
2372 hRet = IStream_Seek(pStreamStc, dlibMove, STREAM_SEEK_SET, &dlibNewPosition);
2374 if (SUCCEEDED(hRet))
2375 hRet = IStream_Read(pStreamStc, pData, cbRequested, (PULONG)&uSize);
2380 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2382 if (clipboard->nFiles <= fileContentsRequest->listIndex)
2384 *((UINT32*)&pData[0]) =
2385 clipboard->fileDescriptor[fileContentsRequest->listIndex]->nFileSizeLow;
2386 *((UINT32*)&pData[4]) =
2387 clipboard->fileDescriptor[fileContentsRequest->listIndex]->nFileSizeHigh;
2388 uSize = cbRequested;
2390 else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE)
2393 if (clipboard->nFiles <= fileContentsRequest->listIndex)
2395 bRet = wf_cliprdr_get_file_contents(
2396 clipboard->file_names[fileContentsRequest->listIndex], pData,
2397 fileContentsRequest->nPositionLow, fileContentsRequest->nPositionHigh, cbRequested,
2402 WLog_ERR(TAG,
"get file contents failed.");
2413 IDataObject_Release(pDataObj);
2422 cliprdr_send_response_filecontents(clipboard, fileContentsRequest->streamId, uSize, pData);
2425 if (sRc != CHANNEL_RC_OK)
2437wf_cliprdr_server_file_contents_response(CliprdrClientContext* context,
2440 if (!context || !fileContentsResponse)
2441 return ERROR_INTERNAL_ERROR;
2443 if (fileContentsResponse->common.msgFlags != CB_RESPONSE_OK)
2446 wfClipboard* clipboard = (wfClipboard*)context->custom;
2449 return ERROR_INTERNAL_ERROR;
2451 clipboard->req_fsize = fileContentsResponse->cbRequested;
2452 clipboard->req_fdata = (
char*)malloc(fileContentsResponse->cbRequested);
2454 if (!clipboard->req_fdata)
2455 return ERROR_INTERNAL_ERROR;
2457 CopyMemory(clipboard->req_fdata, fileContentsResponse->requestedData,
2458 fileContentsResponse->cbRequested);
2460 if (!SetEvent(clipboard->req_fevent))
2462 free(clipboard->req_fdata);
2463 return ERROR_INTERNAL_ERROR;
2466 return CHANNEL_RC_OK;
2469BOOL wf_cliprdr_init(wfContext* wfc, CliprdrClientContext* cliprdr)
2471 wfClipboard* clipboard;
2472 rdpContext* context = (rdpContext*)wfc;
2474 if (!context || !cliprdr)
2477 wfc->clipboard = (wfClipboard*)calloc(1,
sizeof(wfClipboard));
2479 if (!wfc->clipboard)
2482 clipboard = wfc->clipboard;
2483 clipboard->wfc = wfc;
2484 clipboard->context = cliprdr;
2485 clipboard->channels = context->channels;
2486 clipboard->sync = FALSE;
2487 clipboard->map_capacity = 32;
2488 clipboard->map_size = 0;
2489 clipboard->hUser32 = LoadLibraryA(
"user32.dll");
2491 if (clipboard->hUser32)
2493 clipboard->AddClipboardFormatListener = GetProcAddressAs(
2494 clipboard->hUser32,
"AddClipboardFormatListener", fnAddClipboardFormatListener);
2495 clipboard->RemoveClipboardFormatListener = GetProcAddressAs(
2496 clipboard->hUser32,
"RemoveClipboardFormatListener", fnRemoveClipboardFormatListener);
2497 clipboard->GetUpdatedClipboardFormats = GetProcAddressAs(
2498 clipboard->hUser32,
"GetUpdatedClipboardFormats", fnGetUpdatedClipboardFormats);
2501 if (!(clipboard->hUser32 && clipboard->AddClipboardFormatListener &&
2502 clipboard->RemoveClipboardFormatListener && clipboard->GetUpdatedClipboardFormats))
2503 clipboard->legacyApi = TRUE;
2505 if (!(clipboard->format_mappings =
2506 (formatMapping*)calloc(clipboard->map_capacity,
sizeof(formatMapping))))
2509 if (!(clipboard->response_data_event = CreateEvent(
nullptr, TRUE, FALSE,
nullptr)))
2512 if (!(clipboard->req_fevent = CreateEvent(
nullptr, TRUE, FALSE,
nullptr)))
2515 if (!(clipboard->thread = CreateThread(
nullptr, 0, cliprdr_thread_func, clipboard, 0,
nullptr)))
2518 cliprdr->MonitorReady = wf_cliprdr_monitor_ready;
2519 cliprdr->ServerCapabilities = wf_cliprdr_server_capabilities;
2520 cliprdr->ServerFormatList = wf_cliprdr_server_format_list;
2521 cliprdr->ServerFormatListResponse = wf_cliprdr_server_format_list_response;
2522 cliprdr->ServerLockClipboardData = wf_cliprdr_server_lock_clipboard_data;
2523 cliprdr->ServerUnlockClipboardData = wf_cliprdr_server_unlock_clipboard_data;
2524 cliprdr->ServerFormatDataRequest = wf_cliprdr_server_format_data_request;
2525 cliprdr->ServerFormatDataResponse = wf_cliprdr_server_format_data_response;
2526 cliprdr->ServerFileContentsRequest = wf_cliprdr_server_file_contents_request;
2527 cliprdr->ServerFileContentsResponse = wf_cliprdr_server_file_contents_response;
2528 cliprdr->custom = (
void*)wfc->clipboard;
2531 wf_cliprdr_uninit(wfc, cliprdr);
2535BOOL wf_cliprdr_uninit(wfContext* wfc, CliprdrClientContext* cliprdr)
2537 wfClipboard* clipboard;
2539 if (!wfc || !cliprdr)
2542 clipboard = wfc->clipboard;
2547 cliprdr->custom =
nullptr;
2549 if (clipboard->hwnd)
2550 PostMessage(clipboard->hwnd, WM_QUIT, 0, 0);
2552 if (clipboard->thread)
2554 (void)WaitForSingleObject(clipboard->thread, INFINITE);
2555 (void)CloseHandle(clipboard->thread);
2558 if (clipboard->response_data_event)
2559 (void)CloseHandle(clipboard->response_data_event);
2561 if (clipboard->req_fevent)
2562 (void)CloseHandle(clipboard->req_fevent);
2564 clear_file_array(clipboard);
2565 clear_format_map(clipboard);
2566 free(clipboard->format_mappings);