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
19 PWINSTATION_OBJECT WinStaObj; \
20 WinStaObj = PsGetCurrentThreadWin32Thread()->Desktop->WindowStation;
22 #define WINSTA_ClipboardThread WinStaObj->Clipboard->ClipboardThread
23 #define WINSTA_ClipboardOwnerThread WinStaObj->Clipboard->ClipboardOwnerThread
24 #define WINSTA_ClipboardWindow WinStaObj->Clipboard->ClipboardWindow
25 #define WINSTA_ClipboardViewerWindow WinStaObj->Clipboard->ClipboardViewerWindow
26 #define WINSTA_ClipboardOwnerWindow WinStaObj->Clipboard->ClipboardOwnerWindow
27 #define WINSTA_sendDrawClipboardMsg WinStaObj->Clipboard->sendDrawClipboardMsg
28 #define WINSTA_recentlySetClipboard WinStaObj->Clipboard->recentlySetClipboard
29 #define WINSTA_delayedRender WinStaObj->Clipboard->delayedRender
30 #define WINSTA_lastEnumClipboardFormats WinStaObj->Clipboard->lastEnumClipboardFormats
31 #define WINSTA_ClipboardSequenceNumber WinStaObj->Clipboard->ClipboardSequenceNumber
32 #define WINSTA_WindowsChain WinStaObj->Clipboard->WindowsChain
33 #define WINSTA_ClipboardData WinStaObj->Clipboard->ClipboardData
34 #define WINSTA_synthesizedData WinStaObj->Clipboard->synthesizedData
35 #define WINSTA_synthesizedDataSize WinStaObj->Clipboard->synthesizedDataSize
37 PW32THREAD ClipboardThread
;
38 PW32THREAD ClipboardOwnerThread
;
39 PWINDOW_OBJECT ClipboardWindow
;
40 PWINDOW_OBJECT ClipboardViewerWindow
;
41 PWINDOW_OBJECT ClipboardOwnerWindow
;
42 BOOL sendDrawClipboardMsg
;
43 BOOL recentlySetClipboard
;
45 UINT lastEnumClipboardFormats
;
46 DWORD ClipboardSequenceNumber
= 0;
48 PCLIPBOARDCHAINELEMENT WindowsChain
= NULL
;
49 PCLIPBOARDELEMENT ClipboardData
= NULL
;
51 PCHAR synthesizedData
;
52 DWORD synthesizedDataSize
;
55 /*==============================================================*/
57 /* return the pointer to the prev window of the finded window,
58 if NULL does not exists in the chain */
59 PCLIPBOARDCHAINELEMENT FASTCALL
60 IntIsWindowInChain(PWINDOW_OBJECT window
)
62 PCLIPBOARDCHAINELEMENT wce
= WindowsChain
;
66 if (wce
->window
== window
)
76 VOID FASTCALL
printChain()
79 PCLIPBOARDCHAINELEMENT wce2
= WindowsChain
;
82 DPRINT1("chain: %p\n", wce2
->window
->hSelf
);
87 /* the new window always have to be the first in the chain */
88 PCLIPBOARDCHAINELEMENT FASTCALL
89 IntAddWindowToChain(PWINDOW_OBJECT window
)
91 PCLIPBOARDCHAINELEMENT wce
= NULL
;
93 if (!IntIsWindowInChain(window
))
97 wce
= ExAllocatePool(PagedPool
, sizeof(CLIPBOARDCHAINELEMENT
));
100 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
104 wce
->window
= window
;
105 wce
->next
= WindowsChain
;
113 /* return the next window to beremoved later */
117 PCLIPBOARDCHAINELEMENT FASTCALL
118 IntRemoveWindowFromChain(PWINDOW_OBJECT window
)
120 PCLIPBOARDCHAINELEMENT wce
= WindowsChain
;
121 PCLIPBOARDCHAINELEMENT
*link
= &WindowsChain
;
123 if (IntIsWindowInChain(window
))
127 if (wce
->window
== window
)
148 /*==============================================================*/
149 /* if format exists, returns a non zero value (pointing to format object) */
150 PCLIPBOARDELEMENT FASTCALL
151 intIsFormatAvailable(format
)
153 PCLIPBOARDELEMENT ret
= NULL
;
154 PCLIPBOARDELEMENT ce
= ClipboardData
;
158 if (ce
->format
== format
)
168 /* counts how many distinct format were are in the clipboard */
170 IntCountClipboardFormats()
173 PCLIPBOARDELEMENT ce
= ClipboardData
;
183 /* adds a new format and data to the clipboard */
184 PCLIPBOARDELEMENT FASTCALL
185 intAddFormatedData(UINT format
, HANDLE hData
, DWORD size
)
187 PCLIPBOARDELEMENT ce
= NULL
;
189 ce
= ExAllocatePool(PagedPool
, sizeof(CLIPBOARDELEMENT
));
192 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
199 ce
->next
= ClipboardData
;
203 IntIncrementSequenceNumber();
209 /* removes a format and its data from the clipboard */
211 intRemoveFormatedData(UINT format
)
214 PCLIPBOARDELEMENT ce
= ClipboardData
;
215 PCLIPBOARDELEMENT
*link
= &ClipboardData
;
217 if (intIsFormatAvailable(format
))
221 if (ce
->format
== format
)
233 ExFreePool(ce
->hData
);
243 IntEmptyClipboardData()
245 PCLIPBOARDELEMENT ce
= ClipboardData
;
246 PCLIPBOARDELEMENT tmp
;
251 ExFreePool(ce
->hData
);
256 ClipboardData
= NULL
;
259 /*==============================================================*/
262 renderBITMAPfromDIB(LPBYTE hDIB
)
267 BITMAPINFOHEADER
*ih
;
269 //hdc = UserGetDCEx(NULL, NULL, DCX_USESTYLE);
270 hdc
= UserGetDCEx(ClipboardWindow
, NULL
, DCX_USESTYLE
);
272 ih
= (BITMAPINFOHEADER
*)hDIB
;
274 offset
= sizeof(BITMAPINFOHEADER
) + ((ih
->biBitCount
<= 8) ? (sizeof(RGBQUAD
) * (1 << ih
->biBitCount
)) : 0);
276 hbitmap
= NtGdiCreateDIBitmapInternal(hdc
,
287 //UserReleaseDC(NULL, hdc, FALSE);
288 UserReleaseDC(ClipboardWindow
, hdc
, FALSE
);
294 canSinthesize(UINT format
)
301 case CF_METAFILEPICT
:
308 /* returns the size of the sinthesized data */
310 synthesizeData(UINT format
)
314 synthesizedData
= NULL
;
315 synthesizedDataSize
= 0;
317 if (!canSinthesize(format
))
329 case CF_METAFILEPICT
:
341 freeSynthesizedData()
343 ExFreePool(synthesizedData
);
346 /*==============================================================*/
349 intIsClipboardOpenByMe()
351 /* check if we open the clipboard */
352 if (ClipboardThread
&& ClipboardThread
== PsGetCurrentThreadWin32Thread())
354 /* yes, we got a thread and its the same that opens the clipboard */
358 /* will fail if not thread (closed) or not open by me*/
362 /* IntClipboardFreeWindow it's called when a window was destroyed */
364 IntClipboardFreeWindow(PWINDOW_OBJECT window
)
366 /* called from co_UserFreeWindow in window.c */
367 /* check if clipboard is not locked by this window, if yes, unlock it */
368 if (ClipboardThread
== PsGetCurrentThreadWin32Thread())
370 /* the window that opens the clipboard was destroyed */
371 ClipboardThread
= NULL
;
372 ClipboardWindow
= NULL
;
373 //TODO: free clipboard
375 if (window
== ClipboardOwnerWindow
)
377 /* the owner window was destroyed */
378 ClipboardOwnerWindow
= NULL
;
379 ClipboardOwnerThread
= NULL
;
381 /* remove window from window chain */
382 if (IntIsWindowInChain(window
))
384 PCLIPBOARDCHAINELEMENT w
= IntRemoveWindowFromChain(window
);
393 NtUserOpenClipboard(HWND hWnd
, DWORD Unknown1
)
396 PWINDOW_OBJECT Window
;
399 UserEnterExclusive();
401 sendDrawClipboardMsg
= FALSE
;
402 recentlySetClipboard
= FALSE
;
406 /* clipboard is already open */
407 if (ClipboardThread
== PsGetCurrentThreadWin32Thread())
409 if (ClipboardOwnerWindow
)
411 if (ClipboardOwnerWindow
->hSelf
== hWnd
)
430 Window
= UserGetWindowObject(hWnd
);
434 ClipboardWindow
= Window
;
435 ClipboardThread
= PsGetCurrentThreadWin32Thread();
440 ClipboardWindow
= NULL
;
441 ClipboardThread
= NULL
;
442 ClipboardOwnerWindow
= NULL
;
443 ClipboardOwnerThread
= NULL
;
448 ClipboardWindow
= NULL
;
449 ClipboardThread
= PsGetCurrentThreadWin32Thread();
460 NtUserCloseClipboard(VOID
)
464 UserEnterExclusive();
466 if (intIsClipboardOpenByMe())
468 ClipboardWindow
= NULL
;
469 ClipboardThread
= NULL
;
474 SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN
);
477 recentlySetClipboard
= FALSE
;
481 if (sendDrawClipboardMsg
&& WindowsChain
)
483 /* only send message to the first window in the chain, then they'll do the chain */
484 /* commented because it makes a crash in co_MsqSendMessage
485 ASSERT(WindowsChain->window);
486 ASSERT(WindowsChain->window->hSelf);
487 DPRINT1("Clipboard: sending WM_DRAWCLIPBOARD to %p\n", WindowsChain->window->hSelf);
488 co_IntSendMessage(WindowsChain->window->hSelf, WM_DRAWCLIPBOARD, 0, 0);
496 NtUserGetOpenClipboardWindow(VOID
)
504 ret
= ClipboardWindow
->hSelf
;
513 NtUserChangeClipboardChain(HWND hWndRemove
, HWND hWndNewNext
)
516 PCLIPBOARDCHAINELEMENT w
= NULL
;
517 PWINDOW_OBJECT removeWindow
;
518 UserEnterExclusive();
520 removeWindow
= UserGetWindowObject(hWndRemove
);
524 if ((ret
= !!IntIsWindowInChain(removeWindow
)))
526 w
= IntRemoveWindowFromChain(removeWindow
);
534 if (ret
&& WindowsChain
)
536 // only send message to the first window in the chain,
537 // then they do the chain
539 /* WindowsChain->window may be NULL */
540 LPARAM lparam
= WindowsChain
->window
== NULL
? 0 : (LPARAM
)WindowsChain
->window
->hSelf
;
541 DPRINT1("Message: WM_CHANGECBCHAIN to %p", WindowsChain
->window
->hSelf
);
542 co_IntSendMessage(WindowsChain
->window
->hSelf
, WM_CHANGECBCHAIN
, (WPARAM
)hWndRemove
, lparam
);
551 NtUserCountClipboardFormats(VOID
)
557 ret
= IntCountClipboardFormats();
564 NtUserEmptyClipboard(VOID
)
568 UserEnterExclusive();
570 if (intIsClipboardOpenByMe())
574 IntEmptyClipboardData();
577 ClipboardOwnerWindow
= ClipboardWindow
;
578 ClipboardOwnerThread
= ClipboardThread
;
580 IntIncrementSequenceNumber();
586 SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN
);
589 if (ret
&& ClipboardOwnerWindow
)
591 DPRINT("Clipboard: WM_DESTROYCLIPBOARD to %p", ClipboardOwnerWindow
->hSelf
);
592 co_IntSendMessage( ClipboardOwnerWindow
->hSelf
, WM_DESTROYCLIPBOARD
, 0, 0);
601 NtUserGetClipboardData(UINT uFormat
, PVOID pBuffer
)
607 if (intIsClipboardOpenByMe())
609 /* when Unknown1 is zero, we returns to user32 the data size */
612 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
616 /* format exists in clipboard */
617 if (data
->size
== DATA_DELAYED_RENDER
)
619 /* tell owner what data needs to be rendered */
620 if (ClipboardOwnerWindow
)
622 ASSERT(ClipboardOwnerWindow
->hSelf
);
623 co_IntSendMessage(ClipboardOwnerWindow
->hSelf
, WM_RENDERFORMAT
, (WPARAM
)uFormat
, 0);
624 data
= intIsFormatAvailable(uFormat
);
626 ret
= (HANDLE
)(ULONG_PTR
)data
->size
;
631 if (data
->size
== DATA_SYNTHESIZED_RENDER
)
633 data
->size
= synthesizeData(uFormat
);
637 ret
= (HANDLE
)(ULONG_PTR
)data
->size
;
641 /* there is no data in this format */
642 //ret = (HANDLE)FALSE;
647 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
651 if (data
->size
== DATA_DELAYED_RENDER
)
653 // we rendered it in 1st call of getclipboard data
657 if (data
->size
== DATA_SYNTHESIZED_RENDER
)
659 if (uFormat
== CF_BITMAP
)
661 /* BITMAP & METAFILEs returns a GDI handle */
662 PCLIPBOARDELEMENT data
= intIsFormatAvailable(CF_DIB
);
665 ret
= renderBITMAPfromDIB(data
->hData
);
670 ret
= (HANDLE
)pBuffer
;
674 ProbeForWrite(pBuffer
, synthesizedDataSize
, 1);
675 memcpy(pBuffer
, (PCHAR
)synthesizedData
, synthesizedDataSize
);
683 freeSynthesizedData();
688 ret
= (HANDLE
)pBuffer
;
692 ProbeForWrite(pBuffer
, data
->size
, 1);
693 memcpy(pBuffer
, (PCHAR
)data
->hData
, data
->size
);
709 SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN
);
718 NtUserGetClipboardFormatName(UINT format
, PUNICODE_STRING FormatName
,
721 UNICODE_STRING sFormatName
;
724 /* if the format is built-in we fail */
727 /* registetrated formats are >= 0xc000 */
731 if((cchMaxCount
< 1) || !FormatName
)
733 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
739 ProbeForWriteUnicodeString(FormatName
);
740 sFormatName
= *(volatile UNICODE_STRING
*)FormatName
;
741 ProbeForWrite(sFormatName
.Buffer
, sFormatName
.MaximumLength
, 1);
743 ret
= IntGetAtomName((RTL_ATOM
)format
, sFormatName
.Buffer
, cchMaxCount
* sizeof(WCHAR
));
747 ret
= ret
/ sizeof(WCHAR
);
748 sFormatName
.Length
= ret
;
757 SetLastNtError(_SEH_GetExceptionCode());
765 NtUserGetClipboardOwner(VOID
)
771 if (ClipboardOwnerWindow
)
773 ret
= ClipboardOwnerWindow
->hSelf
;
782 NtUserGetClipboardViewer(VOID
)
790 ret
= WindowsChain
->window
->hSelf
;
799 NtUserGetPriorityClipboardFormat(UINT
*paFormatPriorityList
, INT cFormats
)
805 UserEnterExclusive();
809 if (IntCountClipboardFormats() == 0)
815 ProbeForRead(paFormatPriorityList
, cFormats
, sizeof(UINT
));
817 priorityList
= paFormatPriorityList
;
821 for (i
= 0; i
< cFormats
; i
++)
823 if (intIsFormatAvailable(priorityList
[i
]))
825 ret
= priorityList
[i
];
834 SetLastNtError(_SEH_GetExceptionCode());
845 NtUserIsClipboardFormatAvailable(UINT format
)
851 ret
= (intIsFormatAvailable(format
) != NULL
);
861 NtUserSetClipboardData(UINT uFormat
, HANDLE hMem
, DWORD size
)
863 HANDLE hCBData
= NULL
;
864 UNICODE_STRING unicodeString
;
865 OEM_STRING oemString
;
866 ANSI_STRING ansiString
;
868 UserEnterExclusive();
870 /* to place data here the we need to be the owner */
871 if (ClipboardOwnerThread
== PsGetCurrentThreadWin32Thread())
873 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
877 if (data
->size
== DATA_DELAYED_RENDER
)
879 intRemoveFormatedData(uFormat
);
883 // we already have this format on clipboard
892 ProbeForRead(hMem
, size
, 1);
896 SetLastNtError(_SEH_GetExceptionCode());
897 _SEH_YIELD(goto exit_setCB
);
901 if (intIsClipboardOpenByMe())
903 delayedRender
= FALSE
;
906 if (!canSinthesize(uFormat
))
908 hCBData
= ExAllocatePool(PagedPool
, size
);
909 memcpy(hCBData
, hMem
, size
);
910 intAddFormatedData(uFormat
, hCBData
, size
);
911 DPRINT1("Data stored\n");
914 sendDrawClipboardMsg
= TRUE
;
915 recentlySetClipboard
= TRUE
;
916 lastEnumClipboardFormats
= uFormat
;
923 //TODO : sinthesize CF_UNICODETEXT & CF_OEMTEXT
924 // CF_TEXT -> CF_UNICODETEXT
925 ansiString
.Buffer
= hCBData
;
926 ansiString
.Length
= size
;
927 RtlAnsiStringToUnicodeString(&unicodeString
, &ansiString
, TRUE
);
928 intAddFormatedData(CF_UNICODETEXT
, unicodeString
.Buffer
, unicodeString
.Length
* sizeof(WCHAR
));
929 // CF_TEXT -> CF_OEMTEXT
930 RtlUnicodeStringToOemString(&oemString
, &unicodeString
, TRUE
);
931 intAddFormatedData(CF_OEMTEXT
, oemString
.Buffer
, oemString
.Length
);
932 //HKCU\Control Panel\International\Locale
933 //intAddFormatedData(CF_LOCALE, oemString.Buffer, oemString.Length);
938 //TODO : sinthesize CF_TEXT & CF_OEMTEXT
939 //CF_UNICODETEXT -> CF_TEXT
940 unicodeString
.Buffer
= hCBData
;
941 unicodeString
.Length
= size
;
942 RtlUnicodeStringToAnsiString(&ansiString
, &unicodeString
, TRUE
);
943 intAddFormatedData(CF_TEXT
, ansiString
.Buffer
, ansiString
.Length
);
944 //CF_UNICODETEXT -> CF_OEMTEXT
945 RtlUnicodeStringToOemString(&oemString
, &unicodeString
, TRUE
);
946 intAddFormatedData(CF_OEMTEXT
, oemString
.Buffer
, oemString
.Length
);
951 //TODO : sinthesize CF_TEXT & CF_UNICODETEXT
952 //CF_OEMTEXT -> CF_UNICODETEXT
953 oemString
.Buffer
= hCBData
;
954 oemString
.Length
= size
;
955 RtlOemStringToUnicodeString(&unicodeString
, &oemString
, TRUE
);
956 intAddFormatedData(CF_UNICODETEXT
, unicodeString
.Buffer
, unicodeString
.Length
* sizeof(WCHAR
));
957 //CF_OEMTEXT -> CF_TEXT
958 RtlUnicodeStringToAnsiString(&ansiString
, &unicodeString
, TRUE
);
959 intAddFormatedData(CF_TEXT
, ansiString
.Buffer
, ansiString
.Length
);
964 // we need to render the DIB or DIBV5 format as soon as possible
965 // because pallette information may change
971 BITMAPOBJ
*BitmapObj
;
973 hdc
= UserGetDCEx(NULL
, NULL
, DCX_USESTYLE
);
976 BitmapObj
= BITMAPOBJ_LockBitmap(hMem
);
977 BITMAP_GetObject(BitmapObj
, sizeof(BITMAP
), (LPSTR
)&bm
);
980 BITMAPOBJ_UnlockBitmap(BitmapObj
);
983 bi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
984 bi
.bmiHeader
.biWidth
= bm
.bmWidth
;
985 bi
.bmiHeader
.biHeight
= bm
.bmHeight
;
986 bi
.bmiHeader
.biPlanes
= 1;
987 bi
.bmiHeader
.biBitCount
= bm
.bmPlanes
* bm
.bmBitsPixel
;
988 bi
.bmiHeader
.biCompression
= BI_RGB
;
989 bi
.bmiHeader
.biSizeImage
= 0;
990 bi
.bmiHeader
.biXPelsPerMeter
= 0;
991 bi
.bmiHeader
.biYPelsPerMeter
= 0;
992 bi
.bmiHeader
.biClrUsed
= 0;
994 ret
= NtGdiGetDIBitsInternal(hdc
, hMem
, 0, bm
.bmHeight
, NULL
, &bi
, DIB_RGB_COLORS
, 0, 0);
996 size
= bi
.bmiHeader
.biSizeImage
+ sizeof(BITMAPINFOHEADER
);
998 hCBData
= ExAllocatePool(PagedPool
, size
);
999 memcpy(hCBData
, &bi
, sizeof(BITMAPINFOHEADER
));
1001 ret
= NtGdiGetDIBitsInternal(hdc
, hMem
, 0, bm
.bmHeight
, (LPBYTE
)hCBData
+ sizeof(BITMAPINFOHEADER
), &bi
, DIB_RGB_COLORS
, 0, 0);
1003 UserReleaseDC(NULL
, hdc
, FALSE
);
1005 intAddFormatedData(CF_DIB
, hCBData
, size
);
1006 intAddFormatedData(CF_BITMAP
, 0, DATA_SYNTHESIZED_RENDER
);
1007 // intAddFormatedData(CF_DIBV5, hCBData, size);
1013 intAddFormatedData(CF_BITMAP
, 0, DATA_SYNTHESIZED_RENDER
);
1014 // intAddFormatedData(CF_DIBV5, hCBData, size);
1016 // intAddFormatedData(CF_PALETTE, hCBData, size);
1020 // intAddFormatedData(CF_BITMAP, hCBData, size);
1021 // intAddFormatedData(CF_PALETTE, hCBData, size);
1022 // intAddFormatedData(CF_DIB, hCBData, size);
1024 case CF_ENHMETAFILE
:
1025 // intAddFormatedData(CF_METAFILEPICT, hCBData, size);
1027 case CF_METAFILEPICT
:
1028 // intAddFormatedData(CF_ENHMETAFILE, hCBData, size);
1035 // the window provides data in the specified format
1036 delayedRender
= TRUE
;
1037 sendDrawClipboardMsg
= TRUE
;
1038 intAddFormatedData(uFormat
, NULL
, 0);
1039 DPRINT1("SetClipboardData delayed format: %d\n", uFormat
);
1053 NtUserSetClipboardViewer(HWND hWndNewViewer
)
1056 PCLIPBOARDCHAINELEMENT newWC
= NULL
;
1057 PWINDOW_OBJECT window
;
1059 UserEnterExclusive();
1061 window
= UserGetWindowObject(hWndNewViewer
);
1065 if ((newWC
= IntAddWindowToChain(window
)))
1069 // newWC->next may be NULL if we are the first window in the chain
1072 // return the next HWND available window in the chain
1073 ret
= newWC
->next
->window
->hSelf
;
1085 IntEnumClipboardFormats(UINT uFormat
)
1089 if (intIsClipboardOpenByMe())
1093 if (recentlySetClipboard
)
1095 ret
= lastEnumClipboardFormats
;
1099 /* return the first available format */
1102 ret
= ClipboardData
->format
;
1108 if (recentlySetClipboard
)
1114 /* querying nextt available format */
1115 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
1121 ret
= data
->next
->format
;
1125 /* reached the end */
1135 SetLastWin32Error(ERROR_CLIPBOARD_NOT_OPEN
);
1141 // This number is incremented whenever the contents of the clipboard change
1142 // or the clipboard is emptied.
1143 // If clipboard rendering is delayed,
1144 // the sequence number is not incremented until the changes are rendered.
1146 IntIncrementSequenceNumber(VOID
)
1151 WINSTA_ClipboardSequenceNumber
++;
1156 NtUserGetClipboardSequenceNumber(VOID
)
1158 //windowstation sequence number
1159 //if no WINSTA_ACCESSCLIPBOARD access to the window station,
1160 //the function returns zero.
1164 PWINSTATION_OBJECT WinStaObj
;
1167 WinSta
= UserGetProcessWindowStation();
1169 Status
= IntValidateWindowStationHandle(WinSta
, UserMode
, WINSTA_ACCESSCLIPBOARD
, &WinStaObj
);
1171 if (!NT_SUCCESS(Status
))
1173 DPRINT1("No WINSTA_ACCESSCLIPBOARD access\n");
1174 SetLastNtError(Status
);
1178 sn
= WinStaObj
->ClipboardSequenceNumber
;
1180 ObDereferenceObject(WinStaObj
);
1183 //sn = ClipboardSequenceNumber;
1189 /**************** VISTA FUNCTIONS******************/
1191 BOOL STDCALL
NtUserAddClipboardFormatListener(
1199 BOOL STDCALL
NtUserRemoveClipboardFormatListener(
1207 BOOL STDCALL
NtUserGetUpdatedClipboardFormats(