23 #include <freerdp/config.h>
33 #include <winpr/assert.h>
34 #include <winpr/library.h>
36 #include <winpr/crt.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(...) \
58 typedef BOOL(WINAPI* fnAddClipboardFormatListener)(HWND hwnd);
59 typedef BOOL(WINAPI* fnRemoveClipboardFormatListener)(HWND hwnd);
60 typedef 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;
86 ULARGE_INTEGER m_lSize;
87 ULARGE_INTEGER m_lOffset;
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
146 static BOOL wf_create_file_obj(wfClipboard* cliprdrrdr, IDataObject** ppDataObject);
147 static void wf_destroy_file_obj(IDataObject* instance);
148 static UINT32 get_remote_format_id(wfClipboard* clipboard, UINT32 local_format);
149 static UINT cliprdr_send_data_request(wfClipboard* clipboard, UINT32 format);
150 static UINT cliprdr_send_lock(wfClipboard* clipboard);
151 static UINT cliprdr_send_unlock(wfClipboard* clipboard);
152 static UINT cliprdr_send_request_filecontents(wfClipboard* clipboard,
const void* streamid,
153 ULONG index, UINT32 flag, DWORD positionhigh,
154 DWORD positionlow, ULONG request);
156 static void CliprdrDataObject_Delete(CliprdrDataObject* instance);
158 static CliprdrEnumFORMATETC* CliprdrEnumFORMATETC_New(ULONG nFormats, FORMATETC* pFormatEtc);
159 static void CliprdrEnumFORMATETC_Delete(CliprdrEnumFORMATETC* instance);
161 static void CliprdrStream_Delete(CliprdrStream* instance);
163 static BOOL try_open_clipboard(HWND hwnd)
165 for (
size_t x = 0; x < 10; x++)
167 if (OpenClipboard(hwnd))
178 static 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;
194 static ULONG STDMETHODCALLTYPE CliprdrStream_AddRef(IStream* This)
196 CliprdrStream* instance = (CliprdrStream*)This;
201 return InterlockedIncrement(&instance->m_lRefCount);
204 static ULONG STDMETHODCALLTYPE CliprdrStream_Release(IStream* This)
207 CliprdrStream* instance = (CliprdrStream*)This;
212 count = InterlockedDecrement(&instance->m_lRefCount);
216 CliprdrStream_Delete(instance);
225 static HRESULT STDMETHODCALLTYPE CliprdrStream_Read(IStream* This,
void* pv, ULONG cb,
229 CliprdrStream* instance = (CliprdrStream*)This;
230 wfClipboard* clipboard;
232 if (!pv || !pcbRead || !instance)
235 clipboard = (wfClipboard*)instance->m_pData;
238 if (instance->m_lOffset.QuadPart >= instance->m_lSize.QuadPart)
241 ret = cliprdr_send_request_filecontents(clipboard, (
void*)This, instance->m_lIndex,
242 FILECONTENTS_RANGE, instance->m_lOffset.HighPart,
243 instance->m_lOffset.LowPart, cb);
248 if (clipboard->req_fdata)
250 CopyMemory(pv, clipboard->req_fdata, clipboard->req_fsize);
251 free(clipboard->req_fdata);
254 *pcbRead = clipboard->req_fsize;
255 instance->m_lOffset.QuadPart += clipboard->req_fsize;
257 if (clipboard->req_fsize < cb)
263 static HRESULT STDMETHODCALLTYPE CliprdrStream_Write(IStream* This,
const void* pv, ULONG cb,
270 return STG_E_ACCESSDENIED;
273 static HRESULT STDMETHODCALLTYPE CliprdrStream_Seek(IStream* This, LARGE_INTEGER dlibMove,
274 DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
277 CliprdrStream* instance = (CliprdrStream*)This;
282 newoffset = instance->m_lOffset.QuadPart;
286 case STREAM_SEEK_SET:
287 newoffset = dlibMove.QuadPart;
290 case STREAM_SEEK_CUR:
291 newoffset += dlibMove.QuadPart;
294 case STREAM_SEEK_END:
295 newoffset = instance->m_lSize.QuadPart + dlibMove.QuadPart;
302 if (newoffset < 0 || newoffset >= instance->m_lSize.QuadPart)
305 instance->m_lOffset.QuadPart = newoffset;
308 plibNewPosition->QuadPart = instance->m_lOffset.QuadPart;
313 static HRESULT STDMETHODCALLTYPE CliprdrStream_SetSize(IStream* This, ULARGE_INTEGER libNewSize)
320 static HRESULT STDMETHODCALLTYPE CliprdrStream_CopyTo(IStream* This, IStream* pstm,
321 ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead,
322 ULARGE_INTEGER* pcbWritten)
332 static HRESULT STDMETHODCALLTYPE CliprdrStream_Commit(IStream* This, DWORD grfCommitFlags)
335 (void)grfCommitFlags;
339 static HRESULT STDMETHODCALLTYPE CliprdrStream_Revert(IStream* This)
345 static HRESULT STDMETHODCALLTYPE CliprdrStream_LockRegion(IStream* This, ULARGE_INTEGER libOffset,
346 ULARGE_INTEGER cb, DWORD dwLockType)
355 static HRESULT STDMETHODCALLTYPE CliprdrStream_UnlockRegion(IStream* This, ULARGE_INTEGER libOffset,
356 ULARGE_INTEGER cb, DWORD dwLockType)
365 static HRESULT STDMETHODCALLTYPE CliprdrStream_Stat(IStream* This, STATSTG* pstatstg,
368 CliprdrStream* instance = (CliprdrStream*)This;
373 if (pstatstg == NULL)
374 return STG_E_INVALIDPOINTER;
376 ZeroMemory(pstatstg,
sizeof(STATSTG));
380 case STATFLAG_DEFAULT:
381 return STG_E_INSUFFICIENTMEMORY;
383 case STATFLAG_NONAME:
384 pstatstg->cbSize.QuadPart = instance->m_lSize.QuadPart;
385 pstatstg->grfLocksSupported = LOCK_EXCLUSIVE;
386 pstatstg->grfMode = GENERIC_READ;
387 pstatstg->grfStateBits = 0;
388 pstatstg->type = STGTY_STREAM;
391 case STATFLAG_NOOPEN:
392 return STG_E_INVALIDFLAG;
395 return STG_E_INVALIDFLAG;
401 static HRESULT STDMETHODCALLTYPE CliprdrStream_Clone(IStream* This, IStream** ppstm)
408 static CliprdrStream* CliprdrStream_New(ULONG index,
void* pData,
const FILEDESCRIPTORW* dsc)
411 BOOL success = FALSE;
413 CliprdrStream* instance;
414 wfClipboard* clipboard = (wfClipboard*)pData;
415 instance = (CliprdrStream*)calloc(1,
sizeof(CliprdrStream));
419 instance->m_Dsc = *dsc;
420 iStream = &instance->iStream;
421 iStream->lpVtbl = (IStreamVtbl*)calloc(1,
sizeof(IStreamVtbl));
425 iStream->lpVtbl->QueryInterface = CliprdrStream_QueryInterface;
426 iStream->lpVtbl->AddRef = CliprdrStream_AddRef;
427 iStream->lpVtbl->Release = CliprdrStream_Release;
428 iStream->lpVtbl->Read = CliprdrStream_Read;
429 iStream->lpVtbl->Write = CliprdrStream_Write;
430 iStream->lpVtbl->Seek = CliprdrStream_Seek;
431 iStream->lpVtbl->SetSize = CliprdrStream_SetSize;
432 iStream->lpVtbl->CopyTo = CliprdrStream_CopyTo;
433 iStream->lpVtbl->Commit = CliprdrStream_Commit;
434 iStream->lpVtbl->Revert = CliprdrStream_Revert;
435 iStream->lpVtbl->LockRegion = CliprdrStream_LockRegion;
436 iStream->lpVtbl->UnlockRegion = CliprdrStream_UnlockRegion;
437 iStream->lpVtbl->Stat = CliprdrStream_Stat;
438 iStream->lpVtbl->Clone = CliprdrStream_Clone;
439 instance->m_lRefCount = 1;
440 instance->m_lIndex = index;
441 instance->m_pData = pData;
442 instance->m_lOffset.QuadPart = 0;
444 if (instance->m_Dsc.dwFlags & FD_ATTRIBUTES)
446 if (instance->m_Dsc.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
450 if (((instance->m_Dsc.dwFlags & FD_FILESIZE) == 0) && !isDir)
453 if (cliprdr_send_request_filecontents(clipboard, (
void*)instance,
454 instance->m_lIndex, FILECONTENTS_SIZE, 0, 0,
460 instance->m_lSize.QuadPart = *((LONGLONG*)clipboard->req_fdata);
461 free(clipboard->req_fdata);
465 instance->m_lSize.LowPart = instance->m_Dsc.nFileSizeLow;
466 instance->m_lSize.HighPart = instance->m_Dsc.nFileSizeHigh;
474 CliprdrStream_Delete(instance);
481 void CliprdrStream_Delete(CliprdrStream* instance)
485 free(instance->iStream.lpVtbl);
494 static LONG cliprdr_lookup_format(CliprdrDataObject* instance, FORMATETC* pFormatEtc)
496 if (!instance || !pFormatEtc)
499 for (ULONG i = 0; i < instance->m_nNumFormats; i++)
501 if ((pFormatEtc->tymed & instance->m_pFormatEtc[i].tymed) &&
502 pFormatEtc->cfFormat == instance->m_pFormatEtc[i].cfFormat &&
503 pFormatEtc->dwAspect & instance->m_pFormatEtc[i].dwAspect)
512 static HRESULT STDMETHODCALLTYPE CliprdrDataObject_QueryInterface(IDataObject* This, REFIID riid,
520 if (IsEqualIID(riid, &IID_IDataObject) || IsEqualIID(riid, &IID_IUnknown))
522 IDataObject_AddRef(This);
529 return E_NOINTERFACE;
533 static ULONG STDMETHODCALLTYPE CliprdrDataObject_AddRef(IDataObject* This)
535 CliprdrDataObject* instance = (CliprdrDataObject*)This;
540 return InterlockedIncrement(&instance->m_lRefCount);
543 static ULONG STDMETHODCALLTYPE CliprdrDataObject_Release(IDataObject* This)
546 CliprdrDataObject* instance = (CliprdrDataObject*)This;
551 count = InterlockedDecrement(&instance->m_lRefCount);
555 CliprdrDataObject_Delete(instance);
562 static HRESULT STDMETHODCALLTYPE CliprdrDataObject_GetData(IDataObject* This, FORMATETC* pFormatEtc,
566 CliprdrDataObject* instance = (CliprdrDataObject*)This;
567 wfClipboard* clipboard;
569 if (!pFormatEtc || !pMedium || !instance)
572 clipboard = (wfClipboard*)instance->m_pData;
577 if ((idx = cliprdr_lookup_format(instance, pFormatEtc)) == -1)
578 return DV_E_FORMATETC;
580 pMedium->tymed = instance->m_pFormatEtc[idx].tymed;
581 pMedium->pUnkForRelease = 0;
583 if (instance->m_pFormatEtc[idx].cfFormat == RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW))
585 FILEGROUPDESCRIPTOR* dsc;
586 DWORD remote = get_remote_format_id(clipboard, instance->m_pFormatEtc[idx].cfFormat);
588 if (cliprdr_send_data_request(clipboard, remote) != 0)
591 pMedium->hGlobal = clipboard->hmem;
595 dsc = (FILEGROUPDESCRIPTOR*)GlobalLock(clipboard->hmem);
596 instance->m_nStreams = dsc->cItems;
597 GlobalUnlock(clipboard->hmem);
599 if (instance->m_nStreams > 0)
601 if (!instance->m_pStream)
603 instance->m_pStream = (LPSTREAM*)calloc(instance->m_nStreams,
sizeof(LPSTREAM));
605 if (instance->m_pStream)
607 for (ULONG i = 0; i < instance->m_nStreams; i++)
609 instance->m_pStream[i] =
610 (IStream*)CliprdrStream_New(i, clipboard, &dsc->fgd[i]);
612 if (!instance->m_pStream[i])
613 return E_OUTOFMEMORY;
619 if (!instance->m_pStream)
623 GlobalFree(clipboard->hmem);
624 clipboard->hmem = NULL;
627 pMedium->hGlobal = NULL;
628 return E_OUTOFMEMORY;
631 else if (instance->m_pFormatEtc[idx].cfFormat == RegisterClipboardFormat(CFSTR_FILECONTENTS))
633 if ((pFormatEtc->lindex >= 0) && ((ULONG)pFormatEtc->lindex < instance->m_nStreams))
635 pMedium->pstm = instance->m_pStream[pFormatEtc->lindex];
636 IDataObject_AddRef(instance->m_pStream[pFormatEtc->lindex]);
647 static HRESULT STDMETHODCALLTYPE CliprdrDataObject_GetDataHere(IDataObject* This,
648 FORMATETC* pformatetc,
657 static HRESULT STDMETHODCALLTYPE CliprdrDataObject_QueryGetData(IDataObject* This,
658 FORMATETC* pformatetc)
660 CliprdrDataObject* instance = (CliprdrDataObject*)This;
665 if (cliprdr_lookup_format(instance, pformatetc) == -1)
666 return DV_E_FORMATETC;
671 static HRESULT STDMETHODCALLTYPE CliprdrDataObject_GetCanonicalFormatEtc(IDataObject* This,
672 FORMATETC* pformatectIn,
673 FORMATETC* pformatetcOut)
681 pformatetcOut->ptd = NULL;
685 static HRESULT STDMETHODCALLTYPE CliprdrDataObject_SetData(IDataObject* This, FORMATETC* pformatetc,
686 STGMEDIUM* pmedium, BOOL fRelease)
695 static HRESULT STDMETHODCALLTYPE CliprdrDataObject_EnumFormatEtc(IDataObject* This,
697 IEnumFORMATETC** ppenumFormatEtc)
699 CliprdrDataObject* instance = (CliprdrDataObject*)This;
701 if (!instance || !ppenumFormatEtc)
704 if (dwDirection == DATADIR_GET)
706 *ppenumFormatEtc = (IEnumFORMATETC*)CliprdrEnumFORMATETC_New(instance->m_nNumFormats,
707 instance->m_pFormatEtc);
708 return (*ppenumFormatEtc) ? S_OK : E_OUTOFMEMORY;
716 static HRESULT STDMETHODCALLTYPE CliprdrDataObject_DAdvise(IDataObject* This, FORMATETC* pformatetc,
717 DWORD advf, IAdviseSink* pAdvSink,
718 DWORD* pdwConnection)
725 return OLE_E_ADVISENOTSUPPORTED;
728 static HRESULT STDMETHODCALLTYPE CliprdrDataObject_DUnadvise(IDataObject* This, DWORD dwConnection)
732 return OLE_E_ADVISENOTSUPPORTED;
735 static HRESULT STDMETHODCALLTYPE CliprdrDataObject_EnumDAdvise(IDataObject* This,
736 IEnumSTATDATA** ppenumAdvise)
740 return OLE_E_ADVISENOTSUPPORTED;
743 static CliprdrDataObject* CliprdrDataObject_New(FORMATETC* fmtetc, STGMEDIUM* stgmed, ULONG count,
746 CliprdrDataObject* instance;
747 IDataObject* iDataObject;
748 instance = (CliprdrDataObject*)calloc(1,
sizeof(CliprdrDataObject));
753 iDataObject = &instance->iDataObject;
754 iDataObject->lpVtbl = (IDataObjectVtbl*)calloc(1,
sizeof(IDataObjectVtbl));
756 if (!iDataObject->lpVtbl)
759 iDataObject->lpVtbl->QueryInterface = CliprdrDataObject_QueryInterface;
760 iDataObject->lpVtbl->AddRef = CliprdrDataObject_AddRef;
761 iDataObject->lpVtbl->Release = CliprdrDataObject_Release;
762 iDataObject->lpVtbl->GetData = CliprdrDataObject_GetData;
763 iDataObject->lpVtbl->GetDataHere = CliprdrDataObject_GetDataHere;
764 iDataObject->lpVtbl->QueryGetData = CliprdrDataObject_QueryGetData;
765 iDataObject->lpVtbl->GetCanonicalFormatEtc = CliprdrDataObject_GetCanonicalFormatEtc;
766 iDataObject->lpVtbl->SetData = CliprdrDataObject_SetData;
767 iDataObject->lpVtbl->EnumFormatEtc = CliprdrDataObject_EnumFormatEtc;
768 iDataObject->lpVtbl->DAdvise = CliprdrDataObject_DAdvise;
769 iDataObject->lpVtbl->DUnadvise = CliprdrDataObject_DUnadvise;
770 iDataObject->lpVtbl->EnumDAdvise = CliprdrDataObject_EnumDAdvise;
771 instance->m_lRefCount = 1;
772 instance->m_nNumFormats = count;
773 instance->m_pData = data;
774 instance->m_nStreams = 0;
775 instance->m_pStream = NULL;
779 instance->m_pFormatEtc = (FORMATETC*)calloc(count,
sizeof(FORMATETC));
781 if (!instance->m_pFormatEtc)
784 instance->m_pStgMedium = (STGMEDIUM*)calloc(count,
sizeof(STGMEDIUM));
786 if (!instance->m_pStgMedium)
789 for (ULONG i = 0; i < count; i++)
791 instance->m_pFormatEtc[i] = fmtetc[i];
792 instance->m_pStgMedium[i] = stgmed[i];
798 CliprdrDataObject_Delete(instance);
802 void CliprdrDataObject_Delete(CliprdrDataObject* instance)
806 free(instance->iDataObject.lpVtbl);
807 free(instance->m_pFormatEtc);
808 free(instance->m_pStgMedium);
810 if (instance->m_pStream)
812 for (ULONG i = 0; i < instance->m_nStreams; i++)
813 CliprdrStream_Release(instance->m_pStream[i]);
815 free(instance->m_pStream);
822 static BOOL wf_create_file_obj(wfClipboard* clipboard, IDataObject** ppDataObject)
825 STGMEDIUM stgmeds[2];
830 fmtetc[0].cfFormat = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
831 fmtetc[0].dwAspect = DVASPECT_CONTENT;
832 fmtetc[0].lindex = 0;
833 fmtetc[0].ptd = NULL;
834 fmtetc[0].tymed = TYMED_HGLOBAL;
835 stgmeds[0].tymed = TYMED_HGLOBAL;
836 stgmeds[0].hGlobal = NULL;
837 stgmeds[0].pUnkForRelease = NULL;
838 fmtetc[1].cfFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS);
839 fmtetc[1].dwAspect = DVASPECT_CONTENT;
840 fmtetc[1].lindex = 0;
841 fmtetc[1].ptd = NULL;
842 fmtetc[1].tymed = TYMED_ISTREAM;
843 stgmeds[1].tymed = TYMED_ISTREAM;
844 stgmeds[1].pstm = NULL;
845 stgmeds[1].pUnkForRelease = NULL;
846 *ppDataObject = (IDataObject*)CliprdrDataObject_New(fmtetc, stgmeds, 2, clipboard);
847 return (*ppDataObject) ? TRUE : FALSE;
850 static void wf_destroy_file_obj(IDataObject* instance)
853 IDataObject_Release(instance);
860 static void cliprdr_format_deep_copy(FORMATETC* dest, FORMATETC* source)
866 dest->ptd = (DVTARGETDEVICE*)CoTaskMemAlloc(
sizeof(DVTARGETDEVICE));
869 *(dest->ptd) = *(source->ptd);
873 static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_QueryInterface(IEnumFORMATETC* This,
874 REFIID riid,
void** ppvObject)
878 if (IsEqualIID(riid, &IID_IEnumFORMATETC) || IsEqualIID(riid, &IID_IUnknown))
880 IEnumFORMATETC_AddRef(This);
887 return E_NOINTERFACE;
891 static ULONG STDMETHODCALLTYPE CliprdrEnumFORMATETC_AddRef(IEnumFORMATETC* This)
893 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
898 return InterlockedIncrement(&instance->m_lRefCount);
901 static ULONG STDMETHODCALLTYPE CliprdrEnumFORMATETC_Release(IEnumFORMATETC* This)
904 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
909 count = InterlockedDecrement(&instance->m_lRefCount);
913 CliprdrEnumFORMATETC_Delete(instance);
922 static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_Next(IEnumFORMATETC* This, ULONG celt,
923 FORMATETC* rgelt, ULONG* pceltFetched)
926 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
928 if (!instance || !celt || !rgelt)
931 while ((instance->m_nIndex < instance->m_nNumFormats) && (copied < celt))
933 cliprdr_format_deep_copy(&rgelt[copied++], &instance->m_pFormatEtc[instance->m_nIndex++]);
936 if (pceltFetched != 0)
937 *pceltFetched = copied;
939 return (copied == celt) ? S_OK : E_FAIL;
942 static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_Skip(IEnumFORMATETC* This, ULONG celt)
944 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
949 if (instance->m_nIndex + (LONG)celt > instance->m_nNumFormats)
952 instance->m_nIndex += celt;
956 static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_Reset(IEnumFORMATETC* This)
958 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
963 instance->m_nIndex = 0;
967 static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_Clone(IEnumFORMATETC* This,
968 IEnumFORMATETC** ppEnum)
970 CliprdrEnumFORMATETC* instance = (CliprdrEnumFORMATETC*)This;
972 if (!instance || !ppEnum)
976 (IEnumFORMATETC*)CliprdrEnumFORMATETC_New(instance->m_nNumFormats, instance->m_pFormatEtc);
979 return E_OUTOFMEMORY;
981 ((CliprdrEnumFORMATETC*)*ppEnum)->m_nIndex = instance->m_nIndex;
985 CliprdrEnumFORMATETC* CliprdrEnumFORMATETC_New(ULONG nFormats, FORMATETC* pFormatEtc)
987 CliprdrEnumFORMATETC* instance;
988 IEnumFORMATETC* iEnumFORMATETC;
990 if ((nFormats != 0) && !pFormatEtc)
993 instance = (CliprdrEnumFORMATETC*)calloc(1,
sizeof(CliprdrEnumFORMATETC));
998 iEnumFORMATETC = &instance->iEnumFORMATETC;
999 iEnumFORMATETC->lpVtbl = (IEnumFORMATETCVtbl*)calloc(1,
sizeof(IEnumFORMATETCVtbl));
1001 if (!iEnumFORMATETC->lpVtbl)
1004 iEnumFORMATETC->lpVtbl->QueryInterface = CliprdrEnumFORMATETC_QueryInterface;
1005 iEnumFORMATETC->lpVtbl->AddRef = CliprdrEnumFORMATETC_AddRef;
1006 iEnumFORMATETC->lpVtbl->Release = CliprdrEnumFORMATETC_Release;
1007 iEnumFORMATETC->lpVtbl->Next = CliprdrEnumFORMATETC_Next;
1008 iEnumFORMATETC->lpVtbl->Skip = CliprdrEnumFORMATETC_Skip;
1009 iEnumFORMATETC->lpVtbl->Reset = CliprdrEnumFORMATETC_Reset;
1010 iEnumFORMATETC->lpVtbl->Clone = CliprdrEnumFORMATETC_Clone;
1011 instance->m_lRefCount = 1;
1012 instance->m_nIndex = 0;
1013 instance->m_nNumFormats = nFormats;
1017 instance->m_pFormatEtc = (FORMATETC*)calloc(nFormats,
sizeof(FORMATETC));
1019 if (!instance->m_pFormatEtc)
1022 for (ULONG i = 0; i < nFormats; i++)
1023 cliprdr_format_deep_copy(&instance->m_pFormatEtc[i], &pFormatEtc[i]);
1028 CliprdrEnumFORMATETC_Delete(instance);
1032 void CliprdrEnumFORMATETC_Delete(CliprdrEnumFORMATETC* instance)
1036 free(instance->iEnumFORMATETC.lpVtbl);
1038 if (instance->m_pFormatEtc)
1040 for (LONG i = 0; i < instance->m_nNumFormats; i++)
1042 if (instance->m_pFormatEtc[i].ptd)
1043 CoTaskMemFree(instance->m_pFormatEtc[i].ptd);
1046 free(instance->m_pFormatEtc);
1055 static UINT32 get_local_format_id_by_name(wfClipboard* clipboard,
const TCHAR* format_name)
1058 WCHAR* unicode_name;
1059 #if !defined(UNICODE)
1063 if (!clipboard || !format_name)
1066 #if defined(UNICODE)
1067 unicode_name = _wcsdup(format_name);
1069 size = _tcslen(format_name);
1070 unicode_name = calloc(size + 1,
sizeof(WCHAR));
1075 MultiByteToWideChar(CP_OEMCP, 0, format_name, strlen(format_name), unicode_name, size);
1081 for (
size_t i = 0; i < clipboard->map_size; i++)
1083 map = &clipboard->format_mappings[i];
1087 if (wcscmp(map->name, unicode_name) == 0)
1090 return map->local_format_id;
1099 static INLINE BOOL file_transferring(wfClipboard* clipboard)
1101 return get_local_format_id_by_name(clipboard, CFSTR_FILEDESCRIPTORW) ? TRUE : FALSE;
1104 static UINT32 get_remote_format_id(wfClipboard* clipboard, UINT32 local_format)
1111 for (UINT32 i = 0; i < clipboard->map_size; i++)
1113 map = &clipboard->format_mappings[i];
1115 if (map->local_format_id == local_format)
1116 return map->remote_format_id;
1119 return local_format;
1122 static void map_ensure_capacity(wfClipboard* clipboard)
1127 if (clipboard->map_size >= clipboard->map_capacity)
1130 formatMapping* new_map;
1131 new_size = clipboard->map_capacity * 2;
1133 (formatMapping*)realloc(clipboard->format_mappings,
sizeof(formatMapping) * new_size);
1138 clipboard->format_mappings = new_map;
1139 clipboard->map_capacity = new_size;
1143 static BOOL clear_format_map(wfClipboard* clipboard)
1150 if (clipboard->format_mappings)
1152 for (
size_t i = 0; i < clipboard->map_capacity; i++)
1154 map = &clipboard->format_mappings[i];
1155 map->remote_format_id = 0;
1156 map->local_format_id = 0;
1162 clipboard->map_size = 0;
1166 static UINT cliprdr_send_tempdir(wfClipboard* clipboard)
1173 if (GetEnvironmentVariableA(
"TEMP", tempDirectory.szTempDir,
sizeof(tempDirectory.szTempDir)) ==
1177 return clipboard->context->TempDirectory(clipboard->context, &tempDirectory);
1180 static BOOL cliprdr_GetUpdatedClipboardFormats(wfClipboard* clipboard, PUINT lpuiFormats,
1181 UINT cFormats, PUINT pcFormatsOut)
1185 BOOL clipboardOpen = FALSE;
1187 if (!clipboard->legacyApi)
1188 return clipboard->GetUpdatedClipboardFormats(lpuiFormats, cFormats, pcFormatsOut);
1190 clipboardOpen = try_open_clipboard(clipboard->hwnd);
1198 while (index < cFormats)
1200 format = EnumClipboardFormats(format);
1205 lpuiFormats[index] = format;
1209 *pcFormatsOut = index;
1214 static UINT cliprdr_send_format_list(wfClipboard* clipboard)
1218 UINT32 numFormats = 0;
1219 UINT32 formatId = 0;
1220 char formatName[1024];
1225 return ERROR_INTERNAL_ERROR;
1228 if (try_open_clipboard(clipboard->hwnd))
1230 count = CountClipboardFormats();
1231 numFormats = (UINT32)count;
1237 return CHANNEL_RC_NO_MEMORY;
1243 if (IsClipboardFormatAvailable(CF_HDROP))
1245 formats[index++].formatId = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
1246 formats[index++].formatId = RegisterClipboardFormat(CFSTR_FILECONTENTS);
1250 while ((formatId = EnumClipboardFormats(formatId)) != 0)
1251 formats[index++].formatId = formatId;
1257 if (!CloseClipboard())
1260 return ERROR_INTERNAL_ERROR;
1263 for (UINT index = 0; index < numFormats; index++)
1265 if (GetClipboardFormatNameA(formats[index].formatId, formatName,
sizeof(formatName)))
1267 formats[index].formatName = _strdup(formatName);
1272 formatList.numFormats = numFormats;
1273 formatList.formats = formats;
1274 formatList.common.msgType = CB_FORMAT_LIST;
1275 rc = clipboard->context->ClientFormatList(clipboard->context, &formatList);
1277 for (UINT index = 0; index < numFormats; index++)
1278 free(formats[index].formatName);
1284 static UINT cliprdr_send_data_request(wfClipboard* clipboard, UINT32 formatId)
1287 UINT32 remoteFormatId;
1290 if (!clipboard || !clipboard->context || !clipboard->context->ClientFormatDataRequest)
1291 return ERROR_INTERNAL_ERROR;
1293 remoteFormatId = get_remote_format_id(clipboard, formatId);
1295 formatDataRequest.requestedFormatId = remoteFormatId;
1296 clipboard->requestedFormatId = formatId;
1297 rc = clipboard->context->ClientFormatDataRequest(clipboard->context, &formatDataRequest);
1299 if (WaitForSingleObject(clipboard->response_data_event, INFINITE) != WAIT_OBJECT_0)
1300 rc = ERROR_INTERNAL_ERROR;
1301 else if (!ResetEvent(clipboard->response_data_event))
1302 rc = ERROR_INTERNAL_ERROR;
1307 UINT cliprdr_send_request_filecontents(wfClipboard* clipboard,
const void* streamid, ULONG index,
1308 UINT32 flag, DWORD positionhigh, DWORD positionlow,
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 = positionlow;
1321 fileContentsRequest.nPositionHigh = positionhigh;
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;
1335 static UINT cliprdr_send_response_filecontents(wfClipboard* clipboard, UINT32 streamId, UINT32 size,
1340 if (!clipboard || !clipboard->context || !clipboard->context->ClientFileContentsResponse)
1341 return ERROR_INTERNAL_ERROR;
1343 fileContentsResponse.streamId = streamId;
1344 fileContentsResponse.cbRequested = size;
1345 fileContentsResponse.requestedData = data;
1346 fileContentsResponse.common.msgFlags = CB_RESPONSE_OK;
1347 return clipboard->context->ClientFileContentsResponse(clipboard->context,
1348 &fileContentsResponse);
1351 static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
1353 static wfClipboard* clipboard = NULL;
1358 DEBUG_CLIPRDR(
"info: WM_CREATE");
1359 clipboard = (wfClipboard*)((CREATESTRUCT*)lParam)->lpCreateParams;
1360 clipboard->hwnd = hWnd;
1362 if (!clipboard->legacyApi)
1363 clipboard->AddClipboardFormatListener(hWnd);
1365 clipboard->hWndNextViewer = SetClipboardViewer(hWnd);
1370 DEBUG_CLIPRDR(
"info: WM_CLOSE");
1372 if (!clipboard->legacyApi)
1373 clipboard->RemoveClipboardFormatListener(hWnd);
1378 if (clipboard->legacyApi)
1379 ChangeClipboardChain(hWnd, clipboard->hWndNextViewer);
1383 case WM_CLIPBOARDUPDATE:
1384 DEBUG_CLIPRDR(
"info: WM_CLIPBOARDUPDATE");
1386 if (clipboard->sync)
1388 if ((GetClipboardOwner() != clipboard->hwnd) &&
1389 (S_FALSE == OleIsCurrentClipboard(clipboard->data_obj)))
1391 if (clipboard->hmem)
1393 GlobalFree(clipboard->hmem);
1394 clipboard->hmem = NULL;
1397 cliprdr_send_format_list(clipboard);
1403 case WM_RENDERALLFORMATS:
1404 DEBUG_CLIPRDR(
"info: WM_RENDERALLFORMATS");
1407 if (!try_open_clipboard(clipboard->hwnd))
1409 DEBUG_CLIPRDR(
"OpenClipboard failed with 0x%x", GetLastError());
1417 case WM_RENDERFORMAT:
1418 DEBUG_CLIPRDR(
"info: WM_RENDERFORMAT");
1420 if (cliprdr_send_data_request(clipboard, (UINT32)wParam) != 0)
1422 DEBUG_CLIPRDR(
"error: cliprdr_send_data_request failed.");
1426 if (!SetClipboardData((UINT)wParam, clipboard->hmem))
1428 DEBUG_CLIPRDR(
"SetClipboardData failed with 0x%x", GetLastError());
1430 if (clipboard->hmem)
1432 GlobalFree(clipboard->hmem);
1433 clipboard->hmem = NULL;
1440 case WM_DRAWCLIPBOARD:
1441 if (clipboard->legacyApi)
1443 if ((GetClipboardOwner() != clipboard->hwnd) &&
1444 (S_FALSE == OleIsCurrentClipboard(clipboard->data_obj)))
1446 cliprdr_send_format_list(clipboard);
1449 SendMessage(clipboard->hWndNextViewer, Msg, wParam, lParam);
1454 case WM_CHANGECBCHAIN:
1455 if (clipboard->legacyApi)
1457 HWND hWndCurrViewer = (HWND)wParam;
1458 HWND hWndNextViewer = (HWND)lParam;
1460 if (hWndCurrViewer == clipboard->hWndNextViewer)
1461 clipboard->hWndNextViewer = hWndNextViewer;
1462 else if (clipboard->hWndNextViewer)
1463 SendMessage(clipboard->hWndNextViewer, Msg, wParam, lParam);
1468 case WM_CLIPRDR_MESSAGE:
1469 DEBUG_CLIPRDR(
"info: WM_CLIPRDR_MESSAGE");
1473 case OLE_SETCLIPBOARD:
1474 DEBUG_CLIPRDR(
"info: OLE_SETCLIPBOARD");
1476 if (wf_create_file_obj(clipboard, &clipboard->data_obj))
1478 if (OleSetClipboard(clipboard->data_obj) != S_OK)
1480 wf_destroy_file_obj(clipboard->data_obj);
1481 clipboard->data_obj = NULL;
1493 case WM_DESTROYCLIPBOARD:
1494 case WM_ASKCBFORMATNAME:
1495 case WM_HSCROLLCLIPBOARD:
1496 case WM_PAINTCLIPBOARD:
1497 case WM_SIZECLIPBOARD:
1498 case WM_VSCROLLCLIPBOARD:
1500 return DefWindowProc(hWnd, Msg, wParam, lParam);
1506 static int create_cliprdr_window(wfClipboard* clipboard)
1508 WNDCLASSEX wnd_cls = { 0 };
1510 wnd_cls.cbSize =
sizeof(WNDCLASSEX);
1511 wnd_cls.style = CS_OWNDC;
1512 wnd_cls.lpfnWndProc = cliprdr_proc;
1513 wnd_cls.cbClsExtra = 0;
1514 wnd_cls.cbWndExtra = 0;
1515 wnd_cls.hIcon = NULL;
1516 wnd_cls.hCursor = NULL;
1517 wnd_cls.hbrBackground = NULL;
1518 wnd_cls.lpszMenuName = NULL;
1519 wnd_cls.lpszClassName = _T(
"ClipboardHiddenMessageProcessor");
1520 wnd_cls.hInstance = GetModuleHandle(NULL);
1521 wnd_cls.hIconSm = NULL;
1522 RegisterClassEx(&wnd_cls);
1524 CreateWindowEx(WS_EX_LEFT, _T(
"ClipboardHiddenMessageProcessor"), _T(
"rdpclip"), 0, 0, 0, 0,
1525 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), clipboard);
1527 if (!clipboard->hwnd)
1529 DEBUG_CLIPRDR(
"error: CreateWindowEx failed with %x.", GetLastError());
1536 static DWORD WINAPI cliprdr_thread_func(LPVOID arg)
1541 wfClipboard* clipboard = (wfClipboard*)arg;
1544 if ((ret = create_cliprdr_window(clipboard)) != 0)
1547 DEBUG_CLIPRDR(
"error: create clipboard window failed.");
1551 while ((mcode = GetMessage(&msg, 0, 0, 0)) != 0)
1555 DEBUG_CLIPRDR(
"error: clipboard thread GetMessage failed.");
1560 TranslateMessage(&msg);
1561 DispatchMessage(&msg);
1569 static void clear_file_array(wfClipboard* clipboard)
1575 if (clipboard->file_names)
1577 for (
size_t i = 0; i < clipboard->nFiles; i++)
1579 free(clipboard->file_names[i]);
1580 clipboard->file_names[i] = NULL;
1583 free(clipboard->file_names);
1584 clipboard->file_names = NULL;
1588 if (clipboard->fileDescriptor)
1590 for (
size_t i = 0; i < clipboard->nFiles; i++)
1592 free(clipboard->fileDescriptor[i]);
1593 clipboard->fileDescriptor[i] = NULL;
1596 free(clipboard->fileDescriptor);
1597 clipboard->fileDescriptor = NULL;
1600 clipboard->file_array_size = 0;
1601 clipboard->nFiles = 0;
1604 static BOOL wf_cliprdr_get_file_contents(WCHAR* file_name, BYTE* buffer, LONG positionLow,
1605 LONG positionHigh, DWORD nRequested, DWORD* puSize)
1611 if (!file_name || !buffer || !puSize)
1613 WLog_ERR(TAG,
"get file contents Invalid Arguments.");
1617 hFile = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1618 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
1620 if (hFile == INVALID_HANDLE_VALUE)
1623 rc = SetFilePointer(hFile, positionLow, &positionHigh, FILE_BEGIN);
1625 if (rc == INVALID_SET_FILE_POINTER)
1628 if (!ReadFile(hFile, buffer, nRequested, &nGet, NULL))
1630 DEBUG_CLIPRDR(
"ReadFile failed with 0x%08lX.", GetLastError());
1637 if (!CloseHandle(hFile))
1647 static FILEDESCRIPTORW* wf_cliprdr_get_file_descriptor(WCHAR* file_name,
size_t pathLen)
1656 hFile = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
1657 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
1659 if (hFile == INVALID_HANDLE_VALUE)
1665 fd->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI;
1666 fd->dwFileAttributes = GetFileAttributes(file_name);
1668 if (!GetFileTime(hFile, NULL, NULL, &fd->ftLastWriteTime))
1670 fd->dwFlags &= ~FD_WRITESTIME;
1673 fd->nFileSizeLow = GetFileSize(hFile, &fd->nFileSizeHigh);
1674 wcscpy_s(fd->cFileName,
sizeof(fd->cFileName) / 2, file_name + pathLen);
1675 (void)CloseHandle(hFile);
1679 static BOOL wf_cliprdr_array_ensure_capacity(wfClipboard* clipboard)
1684 if (clipboard->nFiles == clipboard->file_array_size)
1689 new_size = (clipboard->file_array_size + 1) * 2;
1694 clipboard->fileDescriptor = new_fd;
1696 new_name = (WCHAR**)realloc(clipboard->file_names, new_size *
sizeof(WCHAR*));
1699 clipboard->file_names = new_name;
1701 if (!new_fd || !new_name)
1704 clipboard->file_array_size = new_size;
1710 static BOOL wf_cliprdr_add_to_file_arrays(wfClipboard* clipboard, WCHAR* full_file_name,
1713 if (!wf_cliprdr_array_ensure_capacity(clipboard))
1717 clipboard->file_names[clipboard->nFiles] = (LPWSTR)malloc(MAX_PATH * 2);
1719 if (!clipboard->file_names[clipboard->nFiles])
1722 wcscpy_s(clipboard->file_names[clipboard->nFiles], MAX_PATH, full_file_name);
1724 clipboard->fileDescriptor[clipboard->nFiles] =
1725 wf_cliprdr_get_file_descriptor(full_file_name, pathLen);
1727 if (!clipboard->fileDescriptor[clipboard->nFiles])
1729 free(clipboard->file_names[clipboard->nFiles]);
1733 clipboard->nFiles++;
1737 static BOOL wf_cliprdr_traverse_directory(wfClipboard* clipboard, WCHAR* Dir,
size_t pathLen)
1740 WCHAR DirSpec[MAX_PATH];
1741 WIN32_FIND_DATA FindFileData;
1743 if (!clipboard || !Dir)
1746 StringCchCopy(DirSpec, MAX_PATH, Dir);
1747 StringCchCat(DirSpec, MAX_PATH, TEXT(
"\\*"));
1748 hFind = FindFirstFile(DirSpec, &FindFileData);
1750 if (hFind == INVALID_HANDLE_VALUE)
1752 DEBUG_CLIPRDR(
"FindFirstFile failed with 0x%x.", GetLastError());
1756 while (FindNextFile(hFind, &FindFileData))
1758 if ((((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) &&
1759 (wcscmp(FindFileData.cFileName, _T(
".")) == 0)) ||
1760 (wcscmp(FindFileData.cFileName, _T(
"..")) == 0))
1765 if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
1767 WCHAR DirAdd[MAX_PATH];
1768 StringCchCopy(DirAdd, MAX_PATH, Dir);
1769 StringCchCat(DirAdd, MAX_PATH, _T(
"\\"));
1770 StringCchCat(DirAdd, MAX_PATH, FindFileData.cFileName);
1772 if (!wf_cliprdr_add_to_file_arrays(clipboard, DirAdd, pathLen))
1775 if (!wf_cliprdr_traverse_directory(clipboard, DirAdd, pathLen))
1780 WCHAR fileName[MAX_PATH];
1781 StringCchCopy(fileName, MAX_PATH, Dir);
1782 StringCchCat(fileName, MAX_PATH, _T(
"\\"));
1783 StringCchCat(fileName, MAX_PATH, FindFileData.cFileName);
1785 if (!wf_cliprdr_add_to_file_arrays(clipboard, fileName, pathLen))
1794 static UINT wf_cliprdr_send_client_capabilities(wfClipboard* clipboard)
1799 if (!clipboard || !clipboard->context || !clipboard->context->ClientCapabilities)
1800 return ERROR_INTERNAL_ERROR;
1802 capabilities.cCapabilitiesSets = 1;
1804 generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
1805 generalCapabilitySet.capabilitySetLength = 12;
1806 generalCapabilitySet.version = CB_CAPS_VERSION_2;
1807 generalCapabilitySet.generalFlags =
1808 CB_USE_LONG_FORMAT_NAMES | CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS;
1809 return clipboard->context->ClientCapabilities(clipboard->context, &capabilities);
1817 static UINT wf_cliprdr_monitor_ready(CliprdrClientContext* context,
1821 wfClipboard* clipboard = (wfClipboard*)context->custom;
1823 if (!context || !monitorReady)
1824 return ERROR_INTERNAL_ERROR;
1826 clipboard->sync = TRUE;
1827 rc = wf_cliprdr_send_client_capabilities(clipboard);
1829 if (rc != CHANNEL_RC_OK)
1832 return cliprdr_send_format_list(clipboard);
1840 static UINT wf_cliprdr_server_capabilities(CliprdrClientContext* context,
1844 wfClipboard* clipboard = (wfClipboard*)context->custom;
1846 if (!context || !capabilities)
1847 return ERROR_INTERNAL_ERROR;
1849 for (UINT32 index = 0; index < capabilities->cCapabilitiesSets; index++)
1851 capabilitySet = &(capabilities->capabilitySets[index]);
1853 if ((capabilitySet->capabilitySetType == CB_CAPSTYPE_GENERAL) &&
1854 (capabilitySet->capabilitySetLength >= CB_CAPSTYPE_GENERAL_LEN))
1858 clipboard->capabilities = generalCapabilitySet->generalFlags;
1863 return CHANNEL_RC_OK;
1871 static UINT wf_cliprdr_server_format_list(CliprdrClientContext* context,
1874 UINT rc = ERROR_INTERNAL_ERROR;
1875 formatMapping* mapping;
1877 wfClipboard* clipboard = (wfClipboard*)context->custom;
1879 if (!clear_format_map(clipboard))
1880 return ERROR_INTERNAL_ERROR;
1882 for (UINT32 i = 0; i < formatList->numFormats; i++)
1884 format = &(formatList->formats[i]);
1885 mapping = &(clipboard->format_mappings[i]);
1886 mapping->remote_format_id = format->formatId;
1888 if (format->formatName)
1890 mapping->name = ConvertUtf8ToWCharAlloc(format->formatName, NULL);
1893 mapping->local_format_id = RegisterClipboardFormatW((LPWSTR)mapping->name);
1897 mapping->name = NULL;
1898 mapping->local_format_id = mapping->remote_format_id;
1901 clipboard->map_size++;
1902 map_ensure_capacity(clipboard);
1905 if (file_transferring(clipboard))
1907 if (PostMessage(clipboard->hwnd, WM_CLIPRDR_MESSAGE, OLE_SETCLIPBOARD, 0))
1912 if (!try_open_clipboard(clipboard->hwnd))
1913 return CHANNEL_RC_OK;
1915 if (EmptyClipboard())
1917 for (UINT32 i = 0; i < (UINT32)clipboard->map_size; i++)
1918 SetClipboardData(clipboard->format_mappings[i].local_format_id, NULL);
1923 if (!CloseClipboard() && GetLastError())
1924 return ERROR_INTERNAL_ERROR;
1936 wf_cliprdr_server_format_list_response(CliprdrClientContext* context,
1940 (void)formatListResponse;
1942 if (formatListResponse->common.msgFlags != CB_RESPONSE_OK)
1945 return CHANNEL_RC_OK;
1954 wf_cliprdr_server_lock_clipboard_data(CliprdrClientContext* context,
1958 (void)lockClipboardData;
1959 return CHANNEL_RC_OK;
1968 wf_cliprdr_server_unlock_clipboard_data(CliprdrClientContext* context,
1972 (void)unlockClipboardData;
1973 return CHANNEL_RC_OK;
1976 static BOOL wf_cliprdr_process_filename(wfClipboard* clipboard, WCHAR* wFileName,
size_t str_len)
1979 size_t offset = str_len;
1981 if (!clipboard || !wFileName)
1987 if (wFileName[offset] == L
'\\')
1993 pathLen = offset + 1;
1995 if (!wf_cliprdr_add_to_file_arrays(clipboard, wFileName, pathLen))
1998 if ((clipboard->fileDescriptor[clipboard->nFiles - 1]->dwFileAttributes &
1999 FILE_ATTRIBUTE_DIRECTORY) != 0)
2002 if (!wf_cliprdr_traverse_directory(clipboard, wFileName, pathLen))
2009 static SSIZE_T wf_cliprdr_tryopen(wfClipboard* clipboard, UINT32 requestedFormatId, BYTE** pData)
2012 WINPR_ASSERT(clipboard);
2013 WINPR_ASSERT(pData);
2018 if (!try_open_clipboard(clipboard->hwnd))
2021 HANDLE hClipdata = GetClipboardData(requestedFormatId);
2026 char* globlemem = (
char*)GlobalLock(hClipdata);
2027 const SSIZE_T size = GlobalSize(hClipdata);
2031 BYTE* buff = malloc(size);
2034 CopyMemory(buff, globlemem, size);
2039 GlobalUnlock(hClipdata);
2047 static SSIZE_T wf_cliprdr_get_filedescriptor(wfClipboard* clipboard, BYTE** pData)
2049 WINPR_ASSERT(clipboard);
2050 WINPR_ASSERT(pData);
2053 LPDATAOBJECT dataObj = NULL;
2054 FORMATETC format_etc = { 0 };
2055 STGMEDIUM stg_medium = { 0 };
2059 HRESULT result = OleGetClipboard(&dataObj);
2064 format_etc.cfFormat = CF_HDROP;
2065 format_etc.tymed = TYMED_HGLOBAL;
2066 format_etc.dwAspect = 1;
2067 format_etc.lindex = -1;
2068 result = IDataObject_GetData(dataObj, &format_etc, &stg_medium);
2072 DEBUG_CLIPRDR(
"dataObj->GetData failed.");
2076 DROPFILES* dropFiles = (DROPFILES*)GlobalLock(stg_medium.hGlobal);
2080 ReleaseStgMedium(&stg_medium);
2081 clipboard->nFiles = 0;
2085 clear_file_array(clipboard);
2087 if (dropFiles->fWide)
2091 for (WCHAR* wFileName = (WCHAR*)((
char*)dropFiles + dropFiles->pFiles);
2092 (len = wcslen(wFileName)) > 0; wFileName += len + 1)
2094 wf_cliprdr_process_filename(clipboard, wFileName, wcslen(wFileName));
2100 for (
char* p = (
char*)((
char*)dropFiles + dropFiles->pFiles); (len = strlen(p)) > 0;
2101 p += len + 1, clipboard->nFiles++)
2103 const int cchWideChar = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, len, NULL, 0);
2104 WCHAR* wFileName = (LPWSTR)calloc(cchWideChar,
sizeof(WCHAR));
2105 MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, len, wFileName, cchWideChar);
2106 wf_cliprdr_process_filename(clipboard, wFileName, cchWideChar);
2111 GlobalUnlock(stg_medium.hGlobal);
2112 ReleaseStgMedium(&stg_medium);
2115 const size_t size = 4ull + clipboard->nFiles *
sizeof(
FILEDESCRIPTORW);
2116 FILEGROUPDESCRIPTORW* groupDsc = (FILEGROUPDESCRIPTORW*)calloc(size, 1);
2120 groupDsc->cItems = clipboard->nFiles;
2122 for (
size_t i = 0; i < clipboard->nFiles; i++)
2124 if (clipboard->fileDescriptor[i])
2125 groupDsc->fgd[i] = *clipboard->fileDescriptor[i];
2128 *pData = (BYTE*)groupDsc;
2133 IDataObject_Release(dataObj);
2143 wf_cliprdr_server_format_data_request(CliprdrClientContext* context,
2148 if (!context || !formatDataRequest)
2149 return ERROR_INTERNAL_ERROR;
2151 wfClipboard* clipboard = (wfClipboard*)context->custom;
2154 return ERROR_INTERNAL_ERROR;
2156 const UINT32 requestedFormatId = formatDataRequest->requestedFormatId;
2158 if (requestedFormatId == RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW))
2160 const SSIZE_T res = wf_cliprdr_get_filedescriptor(clipboard, &response.requestedFormatData);
2162 response.common.dataLen = (UINT32)res;
2167 wf_cliprdr_tryopen(clipboard, requestedFormatId, &response.requestedFormatData);
2169 response.common.dataLen = (UINT32)res;
2172 response.common.msgFlags = CB_RESPONSE_OK;
2174 const UINT rc = clipboard->context->ClientFormatDataResponse(clipboard->context, &response);
2175 free(response.requestedFormatData);
2185 wf_cliprdr_server_format_data_response(CliprdrClientContext* context,
2190 wfClipboard* clipboard;
2192 if (!context || !formatDataResponse)
2193 return ERROR_INTERNAL_ERROR;
2195 clipboard = (wfClipboard*)context->custom;
2198 return ERROR_INTERNAL_ERROR;
2200 if (formatDataResponse->common.msgFlags != CB_RESPONSE_OK)
2202 clipboard->hmem = NULL;
2204 if (!SetEvent(clipboard->response_data_event))
2205 return ERROR_INTERNAL_ERROR;
2207 return CHANNEL_RC_OK;
2210 hMem = GlobalAlloc(GMEM_MOVEABLE, formatDataResponse->common.dataLen);
2213 return ERROR_INTERNAL_ERROR;
2215 data = (BYTE*)GlobalLock(hMem);
2220 return ERROR_INTERNAL_ERROR;
2223 CopyMemory(data, formatDataResponse->requestedFormatData, formatDataResponse->common.dataLen);
2225 if (!GlobalUnlock(hMem) && GetLastError())
2228 return ERROR_INTERNAL_ERROR;
2231 clipboard->hmem = hMem;
2233 if (!SetEvent(clipboard->response_data_event))
2234 return ERROR_INTERNAL_ERROR;
2236 return CHANNEL_RC_OK;
2245 wf_cliprdr_server_file_contents_request(CliprdrClientContext* context,
2250 HRESULT hRet = S_OK;
2251 FORMATETC vFormatEtc = { 0 };
2252 LPDATAOBJECT pDataObj = NULL;
2253 STGMEDIUM vStgMedium = { 0 };
2254 BOOL bIsStreamFile = TRUE;
2255 static LPSTREAM pStreamStc = NULL;
2256 static UINT32 uStreamIdStc = 0;
2257 wfClipboard* clipboard;
2258 UINT rc = ERROR_INTERNAL_ERROR;
2262 if (!context || !fileContentsRequest)
2263 return ERROR_INTERNAL_ERROR;
2265 clipboard = (wfClipboard*)context->custom;
2268 return ERROR_INTERNAL_ERROR;
2270 cbRequested = fileContentsRequest->cbRequested;
2271 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2272 cbRequested =
sizeof(UINT64);
2274 pData = (BYTE*)calloc(1, cbRequested);
2279 hRet = OleGetClipboard(&pDataObj);
2283 WLog_ERR(TAG,
"filecontents: get ole clipboard failed.");
2287 vFormatEtc.cfFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS);
2288 vFormatEtc.tymed = TYMED_ISTREAM;
2289 vFormatEtc.dwAspect = 1;
2290 vFormatEtc.lindex = fileContentsRequest->listIndex;
2291 vFormatEtc.ptd = NULL;
2293 if ((uStreamIdStc != fileContentsRequest->streamId) || !pStreamStc)
2295 LPENUMFORMATETC pEnumFormatEtc;
2297 FORMATETC vFormatEtc2;
2301 IStream_Release(pStreamStc);
2305 bIsStreamFile = FALSE;
2306 hRet = IDataObject_EnumFormatEtc(pDataObj, DATADIR_GET, &pEnumFormatEtc);
2312 hRet = IEnumFORMATETC_Next(pEnumFormatEtc, 1, &vFormatEtc2, &CeltFetched);
2316 if (vFormatEtc2.cfFormat == RegisterClipboardFormat(CFSTR_FILECONTENTS))
2318 hRet = IDataObject_GetData(pDataObj, &vFormatEtc, &vStgMedium);
2322 pStreamStc = vStgMedium.pstm;
2323 uStreamIdStc = fileContentsRequest->streamId;
2324 bIsStreamFile = TRUE;
2330 }
while (hRet == S_OK);
2334 if (bIsStreamFile == TRUE)
2336 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2338 STATSTG vStatStg = { 0 };
2339 hRet = IStream_Stat(pStreamStc, &vStatStg, STATFLAG_NONAME);
2343 *((UINT32*)&pData[0]) = vStatStg.cbSize.LowPart;
2344 *((UINT32*)&pData[4]) = vStatStg.cbSize.HighPart;
2345 uSize = cbRequested;
2348 else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE)
2350 LARGE_INTEGER dlibMove;
2351 ULARGE_INTEGER dlibNewPosition;
2352 dlibMove.HighPart = fileContentsRequest->nPositionHigh;
2353 dlibMove.LowPart = fileContentsRequest->nPositionLow;
2354 hRet = IStream_Seek(pStreamStc, dlibMove, STREAM_SEEK_SET, &dlibNewPosition);
2356 if (SUCCEEDED(hRet))
2357 hRet = IStream_Read(pStreamStc, pData, cbRequested, (PULONG)&uSize);
2362 if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE)
2364 if (clipboard->nFiles <= fileContentsRequest->listIndex)
2366 *((UINT32*)&pData[0]) =
2367 clipboard->fileDescriptor[fileContentsRequest->listIndex]->nFileSizeLow;
2368 *((UINT32*)&pData[4]) =
2369 clipboard->fileDescriptor[fileContentsRequest->listIndex]->nFileSizeHigh;
2370 uSize = cbRequested;
2372 else if (fileContentsRequest->dwFlags == FILECONTENTS_RANGE)
2375 if (clipboard->nFiles <= fileContentsRequest->listIndex)
2377 bRet = wf_cliprdr_get_file_contents(
2378 clipboard->file_names[fileContentsRequest->listIndex], pData,
2379 fileContentsRequest->nPositionLow, fileContentsRequest->nPositionHigh, cbRequested,
2384 WLog_ERR(TAG,
"get file contents failed.");
2395 IDataObject_Release(pDataObj);
2404 cliprdr_send_response_filecontents(clipboard, fileContentsRequest->streamId, uSize, pData);
2407 if (sRc != CHANNEL_RC_OK)
2419 wf_cliprdr_server_file_contents_response(CliprdrClientContext* context,
2422 wfClipboard* clipboard;
2424 if (!context || !fileContentsResponse)
2425 return ERROR_INTERNAL_ERROR;
2427 if (fileContentsResponse->common.msgFlags != CB_RESPONSE_OK)
2430 clipboard = (wfClipboard*)context->custom;
2433 return ERROR_INTERNAL_ERROR;
2435 clipboard->req_fsize = fileContentsResponse->cbRequested;
2436 clipboard->req_fdata = (
char*)malloc(fileContentsResponse->cbRequested);
2438 if (!clipboard->req_fdata)
2439 return ERROR_INTERNAL_ERROR;
2441 CopyMemory(clipboard->req_fdata, fileContentsResponse->requestedData,
2442 fileContentsResponse->cbRequested);
2444 if (!SetEvent(clipboard->req_fevent))
2446 free(clipboard->req_fdata);
2447 return ERROR_INTERNAL_ERROR;
2450 return CHANNEL_RC_OK;
2453 BOOL wf_cliprdr_init(wfContext* wfc, CliprdrClientContext* cliprdr)
2455 wfClipboard* clipboard;
2456 rdpContext* context = (rdpContext*)wfc;
2458 if (!context || !cliprdr)
2461 wfc->clipboard = (wfClipboard*)calloc(1,
sizeof(wfClipboard));
2463 if (!wfc->clipboard)
2466 clipboard = wfc->clipboard;
2467 clipboard->wfc = wfc;
2468 clipboard->context = cliprdr;
2469 clipboard->channels = context->channels;
2470 clipboard->sync = FALSE;
2471 clipboard->map_capacity = 32;
2472 clipboard->map_size = 0;
2473 clipboard->hUser32 = LoadLibraryA(
"user32.dll");
2475 if (clipboard->hUser32)
2477 clipboard->AddClipboardFormatListener = GetProcAddressAs(
2478 clipboard->hUser32,
"AddClipboardFormatListener", fnAddClipboardFormatListener);
2479 clipboard->RemoveClipboardFormatListener = GetProcAddressAs(
2480 clipboard->hUser32,
"RemoveClipboardFormatListener", fnRemoveClipboardFormatListener);
2481 clipboard->GetUpdatedClipboardFormats = GetProcAddressAs(
2482 clipboard->hUser32,
"GetUpdatedClipboardFormats", fnGetUpdatedClipboardFormats);
2485 if (!(clipboard->hUser32 && clipboard->AddClipboardFormatListener &&
2486 clipboard->RemoveClipboardFormatListener && clipboard->GetUpdatedClipboardFormats))
2487 clipboard->legacyApi = TRUE;
2489 if (!(clipboard->format_mappings =
2490 (formatMapping*)calloc(clipboard->map_capacity,
sizeof(formatMapping))))
2493 if (!(clipboard->response_data_event = CreateEvent(NULL, TRUE, FALSE, NULL)))
2496 if (!(clipboard->req_fevent = CreateEvent(NULL, TRUE, FALSE, NULL)))
2499 if (!(clipboard->thread = CreateThread(NULL, 0, cliprdr_thread_func, clipboard, 0, NULL)))
2502 cliprdr->MonitorReady = wf_cliprdr_monitor_ready;
2503 cliprdr->ServerCapabilities = wf_cliprdr_server_capabilities;
2504 cliprdr->ServerFormatList = wf_cliprdr_server_format_list;
2505 cliprdr->ServerFormatListResponse = wf_cliprdr_server_format_list_response;
2506 cliprdr->ServerLockClipboardData = wf_cliprdr_server_lock_clipboard_data;
2507 cliprdr->ServerUnlockClipboardData = wf_cliprdr_server_unlock_clipboard_data;
2508 cliprdr->ServerFormatDataRequest = wf_cliprdr_server_format_data_request;
2509 cliprdr->ServerFormatDataResponse = wf_cliprdr_server_format_data_response;
2510 cliprdr->ServerFileContentsRequest = wf_cliprdr_server_file_contents_request;
2511 cliprdr->ServerFileContentsResponse = wf_cliprdr_server_file_contents_response;
2512 cliprdr->custom = (
void*)wfc->clipboard;
2515 wf_cliprdr_uninit(wfc, cliprdr);
2519 BOOL wf_cliprdr_uninit(wfContext* wfc, CliprdrClientContext* cliprdr)
2521 wfClipboard* clipboard;
2523 if (!wfc || !cliprdr)
2526 clipboard = wfc->clipboard;
2531 cliprdr->custom = NULL;
2533 if (clipboard->hwnd)
2534 PostMessage(clipboard->hwnd, WM_QUIT, 0, 0);
2536 if (clipboard->thread)
2538 (void)WaitForSingleObject(clipboard->thread, INFINITE);
2539 (void)CloseHandle(clipboard->thread);
2542 if (clipboard->response_data_event)
2543 (void)CloseHandle(clipboard->response_data_event);
2545 if (clipboard->req_fevent)
2546 (void)CloseHandle(clipboard->req_fevent);
2548 clear_file_array(clipboard);
2549 clear_format_map(clipboard);
2550 free(clipboard->format_mappings);