2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Clipboard routines
5 * FILE: subsys/win32k/ntuser/clipboard.c
6 * PROGRAMER: Filip Navara <xnavara@volny.cz>
7 * Pablo Borobia <pborobia@gmail.com>
15 #define DATA_DELAYED_RENDER 0
16 #define DATA_SYNTHESIZED_RENDER -1
18 PTHREADINFO ClipboardThread
;
19 PTHREADINFO ClipboardOwnerThread
;
20 PWINDOW_OBJECT ClipboardWindow
;
21 PWINDOW_OBJECT ClipboardViewerWindow
;
22 PWINDOW_OBJECT ClipboardOwnerWindow
;
23 BOOL sendDrawClipboardMsg
;
24 BOOL recentlySetClipboard
;
26 UINT lastEnumClipboardFormats
;
27 DWORD ClipboardSequenceNumber
= 0;
29 PCLIPBOARDCHAINELEMENT WindowsChain
= NULL
;
30 PCLIPBOARDELEMENT ClipboardData
= NULL
;
32 PCHAR synthesizedData
;
33 DWORD synthesizedDataSize
;
36 /*==============================================================*/
38 /* return the pointer to the prev window of the finded window,
39 if NULL does not exists in the chain */
40 PCLIPBOARDCHAINELEMENT FASTCALL
41 IntIsWindowInChain(PWINDOW_OBJECT window
)
43 PCLIPBOARDCHAINELEMENT wce
= WindowsChain
;
47 if (wce
->window
== window
)
57 VOID FASTCALL
printChain(VOID
)
60 PCLIPBOARDCHAINELEMENT wce2
= WindowsChain
;
63 DPRINT1("chain: %p\n", wce2
->window
->hSelf
);
68 /* the new window always have to be the first in the chain */
69 PCLIPBOARDCHAINELEMENT FASTCALL
70 IntAddWindowToChain(PWINDOW_OBJECT window
)
72 PCLIPBOARDCHAINELEMENT wce
= NULL
;
74 if (!IntIsWindowInChain(window
))
78 wce
= ExAllocatePool(PagedPool
, sizeof(CLIPBOARDCHAINELEMENT
));
81 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
86 wce
->next
= WindowsChain
;
94 /* return the next window to beremoved later */
98 PCLIPBOARDCHAINELEMENT FASTCALL
99 IntRemoveWindowFromChain(PWINDOW_OBJECT window
)
101 PCLIPBOARDCHAINELEMENT wce
= WindowsChain
;
102 PCLIPBOARDCHAINELEMENT
*link
= &WindowsChain
;
104 if (IntIsWindowInChain(window
))
108 if (wce
->window
== window
)
129 /*==============================================================*/
130 /* if format exists, returns a non zero value (pointing to format object) */
131 PCLIPBOARDELEMENT FASTCALL
132 intIsFormatAvailable(UINT format
)
134 PCLIPBOARDELEMENT ret
= NULL
;
135 PCLIPBOARDELEMENT ce
= ClipboardData
;
139 if (ce
->format
== format
)
149 /* counts how many distinct format were are in the clipboard */
151 IntCountClipboardFormats(VOID
)
154 PCLIPBOARDELEMENT ce
= ClipboardData
;
164 /* adds a new format and data to the clipboard */
165 PCLIPBOARDELEMENT FASTCALL
166 intAddFormatedData(UINT format
, HANDLE hData
, DWORD size
)
168 PCLIPBOARDELEMENT ce
= NULL
;
170 ce
= ExAllocatePool(PagedPool
, sizeof(CLIPBOARDELEMENT
));
173 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
180 ce
->next
= ClipboardData
;
184 IntIncrementSequenceNumber();
190 /* removes a format and its data from the clipboard */
192 intRemoveFormatedData(UINT format
)
195 PCLIPBOARDELEMENT ce
= ClipboardData
;
196 PCLIPBOARDELEMENT
*link
= &ClipboardData
;
198 if (intIsFormatAvailable(format
))
202 if (ce
->format
== format
)
214 ExFreePool(ce
->hData
);
224 IntEmptyClipboardData(VOID
)
226 PCLIPBOARDELEMENT ce
= ClipboardData
;
227 PCLIPBOARDELEMENT tmp
;
234 ExFreePool(ce
->hData
);
240 ClipboardData
= NULL
;
243 /*==============================================================*/
246 renderBITMAPfromDIB(LPBYTE hDIB
)
251 BITMAPINFOHEADER
*ih
;
253 //hdc = UserGetDCEx(NULL, NULL, DCX_USESTYLE);
254 hdc
= UserGetDCEx(ClipboardWindow
, NULL
, DCX_USESTYLE
);
256 ih
= (BITMAPINFOHEADER
*)hDIB
;
258 offset
= sizeof(BITMAPINFOHEADER
) + ((ih
->biBitCount
<= 8) ? (sizeof(RGBQUAD
) * (1 << ih
->biBitCount
)) : 0);
260 hbitmap
= NtGdiCreateDIBitmapInternal(hdc
,
271 //UserReleaseDC(NULL, hdc, FALSE);
272 UserReleaseDC(ClipboardWindow
, hdc
, FALSE
);
278 canSinthesize(UINT format
)
285 case CF_METAFILEPICT
:
292 /* returns the size of the sinthesized data */
294 synthesizeData(UINT format
)
298 synthesizedData
= NULL
;
299 synthesizedDataSize
= 0;
301 if (!canSinthesize(format
))
313 case CF_METAFILEPICT
:
325 freeSynthesizedData(VOID
)
327 ExFreePool(synthesizedData
);
330 /*==============================================================*/
333 intIsClipboardOpenByMe(VOID
)
335 /* check if we open the clipboard */
336 if (ClipboardThread
&& ClipboardThread
== PsGetCurrentThreadWin32Thread())
338 /* yes, we got a thread and its the same that opens the clipboard */
342 /* will fail if not thread (closed) or not open by me*/
346 /* IntClipboardFreeWindow it's called when a window was destroyed */
348 IntClipboardFreeWindow(PWINDOW_OBJECT window
)
350 /* called from co_UserFreeWindow in window.c */
351 /* check if clipboard is not locked by this window, if yes, unlock it */
352 if (ClipboardThread
== PsGetCurrentThreadWin32Thread())
354 /* the window that opens the clipboard was destroyed */
355 ClipboardThread
= NULL
;
356 ClipboardWindow
= NULL
;
357 //TODO: free clipboard
359 if (window
== ClipboardOwnerWindow
)
361 /* the owner window was destroyed */
362 ClipboardOwnerWindow
= NULL
;
363 ClipboardOwnerThread
= NULL
;
365 /* remove window from window chain */
366 if (IntIsWindowInChain(window
))
368 PCLIPBOARDCHAINELEMENT w
= IntRemoveWindowFromChain(window
);
377 NtUserOpenClipboard(HWND hWnd
, DWORD Unknown1
)
380 PWINDOW_OBJECT Window
;
383 UserEnterExclusive();
385 sendDrawClipboardMsg
= FALSE
;
386 recentlySetClipboard
= FALSE
;
390 /* clipboard is already open */
391 if (ClipboardThread
== PsGetCurrentThreadWin32Thread())
393 if (ClipboardOwnerWindow
)
395 if (ClipboardOwnerWindow
->hSelf
== hWnd
)
414 Window
= UserGetWindowObject(hWnd
);
418 ClipboardWindow
= Window
;
419 ClipboardThread
= PsGetCurrentThreadWin32Thread();
424 ClipboardWindow
= NULL
;
425 ClipboardThread
= NULL
;
426 ClipboardOwnerWindow
= NULL
;
427 ClipboardOwnerThread
= NULL
;
432 ClipboardWindow
= NULL
;
433 ClipboardThread
= PsGetCurrentThreadWin32Thread();
444 NtUserCloseClipboard(VOID
)
448 UserEnterExclusive();
450 if (intIsClipboardOpenByMe())
452 ClipboardWindow
= NULL
;
453 ClipboardThread
= NULL
;
458 SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN
);
461 recentlySetClipboard
= FALSE
;
465 if (sendDrawClipboardMsg
&& WindowsChain
)
467 /* only send message to the first window in the chain, then they'll do the chain */
468 /* commented because it makes a crash in co_MsqSendMessage
469 ASSERT(WindowsChain->window);
470 ASSERT(WindowsChain->window->hSelf);
471 DPRINT1("Clipboard: sending WM_DRAWCLIPBOARD to %p\n", WindowsChain->window->hSelf);
472 co_IntSendMessage(WindowsChain->window->hSelf, WM_DRAWCLIPBOARD, 0, 0);
480 NtUserGetOpenClipboardWindow(VOID
)
488 ret
= ClipboardWindow
->hSelf
;
497 NtUserChangeClipboardChain(HWND hWndRemove
, HWND hWndNewNext
)
500 PCLIPBOARDCHAINELEMENT w
= NULL
;
501 PWINDOW_OBJECT removeWindow
;
502 UserEnterExclusive();
504 removeWindow
= UserGetWindowObject(hWndRemove
);
508 if ((ret
= !!IntIsWindowInChain(removeWindow
)))
510 w
= IntRemoveWindowFromChain(removeWindow
);
518 if (ret
&& WindowsChain
)
520 // only send message to the first window in the chain,
521 // then they do the chain
523 /* WindowsChain->window may be NULL */
524 LPARAM lparam
= WindowsChain
->window
== NULL
? 0 : (LPARAM
)WindowsChain
->window
->hSelf
;
525 DPRINT1("Message: WM_CHANGECBCHAIN to %p", WindowsChain
->window
->hSelf
);
526 co_IntSendMessage(WindowsChain
->window
->hSelf
, WM_CHANGECBCHAIN
, (WPARAM
)hWndRemove
, lparam
);
535 NtUserCountClipboardFormats(VOID
)
541 ret
= IntCountClipboardFormats();
548 NtUserEmptyClipboard(VOID
)
552 UserEnterExclusive();
554 if (intIsClipboardOpenByMe())
558 IntEmptyClipboardData();
561 ClipboardOwnerWindow
= ClipboardWindow
;
562 ClipboardOwnerThread
= ClipboardThread
;
564 IntIncrementSequenceNumber();
570 SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN
);
573 if (ret
&& ClipboardOwnerWindow
)
575 DPRINT("Clipboard: WM_DESTROYCLIPBOARD to %p", ClipboardOwnerWindow
->hSelf
);
576 co_IntSendMessage( ClipboardOwnerWindow
->hSelf
, WM_DESTROYCLIPBOARD
, 0, 0);
585 NtUserGetClipboardData(UINT uFormat
, PVOID pBuffer
)
591 if (intIsClipboardOpenByMe())
593 /* when Unknown1 is zero, we returns to user32 the data size */
596 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
600 /* format exists in clipboard */
601 if (data
->size
== DATA_DELAYED_RENDER
)
603 /* tell owner what data needs to be rendered */
604 if (ClipboardOwnerWindow
)
606 ASSERT(ClipboardOwnerWindow
->hSelf
);
607 co_IntSendMessage(ClipboardOwnerWindow
->hSelf
, WM_RENDERFORMAT
, (WPARAM
)uFormat
, 0);
608 data
= intIsFormatAvailable(uFormat
);
610 ret
= (HANDLE
)(ULONG_PTR
)data
->size
;
615 if (data
->size
== DATA_SYNTHESIZED_RENDER
)
617 data
->size
= synthesizeData(uFormat
);
621 ret
= (HANDLE
)(ULONG_PTR
)data
->size
;
625 /* there is no data in this format */
626 //ret = (HANDLE)FALSE;
631 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
635 if (data
->size
== DATA_DELAYED_RENDER
)
637 // we rendered it in 1st call of getclipboard data
641 if (data
->size
== DATA_SYNTHESIZED_RENDER
)
643 if (uFormat
== CF_BITMAP
)
645 /* BITMAP & METAFILEs returns a GDI handle */
646 PCLIPBOARDELEMENT data
= intIsFormatAvailable(CF_DIB
);
649 ret
= renderBITMAPfromDIB(data
->hData
);
654 ret
= (HANDLE
)pBuffer
;
658 ProbeForWrite(pBuffer
, synthesizedDataSize
, 1);
659 memcpy(pBuffer
, (PCHAR
)synthesizedData
, synthesizedDataSize
);
661 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
667 freeSynthesizedData();
672 ret
= (HANDLE
)pBuffer
;
676 ProbeForWrite(pBuffer
, data
->size
, 1);
677 memcpy(pBuffer
, (PCHAR
)data
->hData
, data
->size
);
679 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
693 SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN
);
702 NtUserGetClipboardFormatName(UINT format
, PUNICODE_STRING FormatName
,
705 UNICODE_STRING sFormatName
;
708 /* if the format is built-in we fail */
711 /* registetrated formats are >= 0xc000 */
715 if((cchMaxCount
< 1) || !FormatName
)
717 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
723 ProbeForWriteUnicodeString(FormatName
);
724 sFormatName
= *(volatile UNICODE_STRING
*)FormatName
;
725 ProbeForWrite(sFormatName
.Buffer
, sFormatName
.MaximumLength
, 1);
727 ret
= IntGetAtomName((RTL_ATOM
)format
, sFormatName
.Buffer
, cchMaxCount
* sizeof(WCHAR
));
731 ret
= ret
/ sizeof(WCHAR
);
732 sFormatName
.Length
= ret
;
739 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
741 SetLastNtError(_SEH2_GetExceptionCode());
749 NtUserGetClipboardOwner(VOID
)
755 if (ClipboardOwnerWindow
)
757 ret
= ClipboardOwnerWindow
->hSelf
;
766 NtUserGetClipboardViewer(VOID
)
774 ret
= WindowsChain
->window
->hSelf
;
783 NtUserGetPriorityClipboardFormat(UINT
*paFormatPriorityList
, INT cFormats
)
789 UserEnterExclusive();
793 if (IntCountClipboardFormats() == 0)
799 ProbeForRead(paFormatPriorityList
, cFormats
, sizeof(UINT
));
801 priorityList
= paFormatPriorityList
;
805 for (i
= 0; i
< cFormats
; i
++)
807 if (intIsFormatAvailable(priorityList
[i
]))
809 ret
= priorityList
[i
];
816 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
818 SetLastNtError(_SEH2_GetExceptionCode());
829 NtUserIsClipboardFormatAvailable(UINT format
)
835 ret
= (intIsFormatAvailable(format
) != NULL
);
845 NtUserSetClipboardData(UINT uFormat
, HANDLE hMem
, DWORD size
)
847 HANDLE hCBData
= NULL
;
848 UNICODE_STRING unicodeString
;
849 OEM_STRING oemString
;
850 ANSI_STRING ansiString
;
852 UserEnterExclusive();
854 /* to place data here the we need to be the owner */
855 if (ClipboardOwnerThread
== PsGetCurrentThreadWin32Thread())
857 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
861 if (data
->size
== DATA_DELAYED_RENDER
)
863 intRemoveFormatedData(uFormat
);
867 // we already have this format on clipboard
876 ProbeForRead(hMem
, size
, 1);
878 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
880 SetLastNtError(_SEH2_GetExceptionCode());
881 _SEH2_YIELD(goto exit_setCB
);
885 if (intIsClipboardOpenByMe())
887 delayedRender
= FALSE
;
890 if (!canSinthesize(uFormat
))
892 hCBData
= ExAllocatePool(PagedPool
, size
);
893 memcpy(hCBData
, hMem
, size
);
894 intAddFormatedData(uFormat
, hCBData
, size
);
895 DPRINT1("Data stored\n");
898 sendDrawClipboardMsg
= TRUE
;
899 recentlySetClipboard
= TRUE
;
900 lastEnumClipboardFormats
= uFormat
;
907 //TODO : sinthesize CF_UNICODETEXT & CF_OEMTEXT
908 // CF_TEXT -> CF_UNICODETEXT
909 ansiString
.Buffer
= hCBData
;
910 ansiString
.Length
= size
;
911 RtlAnsiStringToUnicodeString(&unicodeString
, &ansiString
, TRUE
);
912 intAddFormatedData(CF_UNICODETEXT
, unicodeString
.Buffer
, unicodeString
.Length
* sizeof(WCHAR
));
913 // CF_TEXT -> CF_OEMTEXT
914 RtlUnicodeStringToOemString(&oemString
, &unicodeString
, TRUE
);
915 intAddFormatedData(CF_OEMTEXT
, oemString
.Buffer
, oemString
.Length
);
916 //HKCU\Control Panel\International\Locale
917 //intAddFormatedData(CF_LOCALE, oemString.Buffer, oemString.Length);
922 //TODO : sinthesize CF_TEXT & CF_OEMTEXT
923 //CF_UNICODETEXT -> CF_TEXT
924 unicodeString
.Buffer
= hCBData
;
925 unicodeString
.Length
= size
;
926 RtlUnicodeStringToAnsiString(&ansiString
, &unicodeString
, TRUE
);
927 intAddFormatedData(CF_TEXT
, ansiString
.Buffer
, ansiString
.Length
);
928 //CF_UNICODETEXT -> CF_OEMTEXT
929 RtlUnicodeStringToOemString(&oemString
, &unicodeString
, TRUE
);
930 intAddFormatedData(CF_OEMTEXT
, oemString
.Buffer
, oemString
.Length
);
935 //TODO : sinthesize CF_TEXT & CF_UNICODETEXT
936 //CF_OEMTEXT -> CF_UNICODETEXT
937 oemString
.Buffer
= hCBData
;
938 oemString
.Length
= size
;
939 RtlOemStringToUnicodeString(&unicodeString
, &oemString
, TRUE
);
940 intAddFormatedData(CF_UNICODETEXT
, unicodeString
.Buffer
, unicodeString
.Length
* sizeof(WCHAR
));
941 //CF_OEMTEXT -> CF_TEXT
942 RtlUnicodeStringToAnsiString(&ansiString
, &unicodeString
, TRUE
);
943 intAddFormatedData(CF_TEXT
, ansiString
.Buffer
, ansiString
.Length
);
948 // we need to render the DIB or DIBV5 format as soon as possible
949 // because pallette information may change
957 hdc
= UserGetDCEx(NULL
, NULL
, DCX_USESTYLE
);
960 psurf
= SURFACE_LockSurface(hMem
);
961 BITMAP_GetObject(psurf
, sizeof(BITMAP
), (PVOID
)&bm
);
964 SURFACE_UnlockSurface(psurf
);
967 bi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
968 bi
.bmiHeader
.biWidth
= bm
.bmWidth
;
969 bi
.bmiHeader
.biHeight
= bm
.bmHeight
;
970 bi
.bmiHeader
.biPlanes
= 1;
971 bi
.bmiHeader
.biBitCount
= bm
.bmPlanes
* bm
.bmBitsPixel
;
972 bi
.bmiHeader
.biCompression
= BI_RGB
;
973 bi
.bmiHeader
.biSizeImage
= 0;
974 bi
.bmiHeader
.biXPelsPerMeter
= 0;
975 bi
.bmiHeader
.biYPelsPerMeter
= 0;
976 bi
.bmiHeader
.biClrUsed
= 0;
978 ret
= NtGdiGetDIBitsInternal(hdc
, hMem
, 0, bm
.bmHeight
, NULL
, &bi
, DIB_RGB_COLORS
, 0, 0);
980 size
= bi
.bmiHeader
.biSizeImage
+ sizeof(BITMAPINFOHEADER
);
982 hCBData
= ExAllocatePool(PagedPool
, size
);
983 memcpy(hCBData
, &bi
, sizeof(BITMAPINFOHEADER
));
985 ret
= NtGdiGetDIBitsInternal(hdc
, hMem
, 0, bm
.bmHeight
, (LPBYTE
)hCBData
+ sizeof(BITMAPINFOHEADER
), &bi
, DIB_RGB_COLORS
, 0, 0);
987 UserReleaseDC(NULL
, hdc
, FALSE
);
989 intAddFormatedData(CF_DIB
, hCBData
, size
);
990 intAddFormatedData(CF_BITMAP
, 0, DATA_SYNTHESIZED_RENDER
);
991 // intAddFormatedData(CF_DIBV5, hCBData, size);
997 intAddFormatedData(CF_BITMAP
, 0, DATA_SYNTHESIZED_RENDER
);
998 // intAddFormatedData(CF_DIBV5, hCBData, size);
1000 // intAddFormatedData(CF_PALETTE, hCBData, size);
1004 // intAddFormatedData(CF_BITMAP, hCBData, size);
1005 // intAddFormatedData(CF_PALETTE, hCBData, size);
1006 // intAddFormatedData(CF_DIB, hCBData, size);
1008 case CF_ENHMETAFILE
:
1009 // intAddFormatedData(CF_METAFILEPICT, hCBData, size);
1011 case CF_METAFILEPICT
:
1012 // intAddFormatedData(CF_ENHMETAFILE, hCBData, size);
1019 // the window provides data in the specified format
1020 delayedRender
= TRUE
;
1021 sendDrawClipboardMsg
= TRUE
;
1022 intAddFormatedData(uFormat
, NULL
, 0);
1023 DPRINT1("SetClipboardData delayed format: %d\n", uFormat
);
1037 NtUserSetClipboardViewer(HWND hWndNewViewer
)
1040 PCLIPBOARDCHAINELEMENT newWC
= NULL
;
1041 PWINDOW_OBJECT window
;
1043 UserEnterExclusive();
1045 window
= UserGetWindowObject(hWndNewViewer
);
1049 if ((newWC
= IntAddWindowToChain(window
)))
1053 // newWC->next may be NULL if we are the first window in the chain
1056 // return the next HWND available window in the chain
1057 ret
= newWC
->next
->window
->hSelf
;
1069 IntEnumClipboardFormats(UINT uFormat
)
1073 if (intIsClipboardOpenByMe())
1077 if (recentlySetClipboard
)
1079 ret
= lastEnumClipboardFormats
;
1083 /* return the first available format */
1086 ret
= ClipboardData
->format
;
1092 if (recentlySetClipboard
)
1098 /* querying nextt available format */
1099 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
1105 ret
= data
->next
->format
;
1109 /* reached the end */
1119 SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN
);
1125 // This number is incremented whenever the contents of the clipboard change
1126 // or the clipboard is emptied.
1127 // If clipboard rendering is delayed,
1128 // the sequence number is not incremented until the changes are rendered.
1130 IntIncrementSequenceNumber(VOID
)
1133 PWINSTATION_OBJECT WinStaObj
;
1135 pti
= PsGetCurrentThreadWin32Thread();
1136 WinStaObj
= pti
->rpdesk
->rpwinstaParent
;
1138 WinStaObj
->Clipboard
->ClipboardSequenceNumber
++;
1142 NtUserGetClipboardSequenceNumber(VOID
)
1144 //windowstation sequence number
1145 //if no WINSTA_ACCESSCLIPBOARD access to the window station,
1146 //the function returns zero.
1150 PWINSTATION_OBJECT WinStaObj
;
1153 WinSta
= UserGetProcessWindowStation();
1155 Status
= IntValidateWindowStationHandle(WinSta
, KernelMode
, WINSTA_ACCESSCLIPBOARD
, &WinStaObj
);
1157 if (!NT_SUCCESS(Status
))
1159 DPRINT1("No WINSTA_ACCESSCLIPBOARD access\n");
1160 SetLastNtError(Status
);
1164 sn
= WinStaObj
->ClipboardSequenceNumber
;
1166 ObDereferenceObject(WinStaObj
);
1169 //sn = ClipboardSequenceNumber;
1175 /**************** VISTA FUNCTIONS******************/
1177 BOOL APIENTRY
NtUserAddClipboardFormatListener(
1185 BOOL APIENTRY
NtUserRemoveClipboardFormatListener(
1193 BOOL APIENTRY
NtUserGetUpdatedClipboardFormats(