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
;
21 PWND ClipboardViewerWindow
;
22 PWND 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(PWND 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
->head
.h
);
68 /* the new window always have to be the first in the chain */
69 PCLIPBOARDCHAINELEMENT FASTCALL
70 IntAddWindowToChain(PWND window
)
72 PCLIPBOARDCHAINELEMENT wce
= NULL
;
74 if (!IntIsWindowInChain(window
))
78 wce
= ExAllocatePoolWithTag(PagedPool
, sizeof(CLIPBOARDCHAINELEMENT
), USERTAG_CLIPBOARD
);
81 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
86 wce
->next
= WindowsChain
;
94 /* return the next window to beremoved later */
98 PCLIPBOARDCHAINELEMENT FASTCALL
99 IntRemoveWindowFromChain(PWND 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
= ExAllocatePoolWithTag(PagedPool
, sizeof(CLIPBOARDELEMENT
), USERTAG_CLIPBOARD
);
173 EngSetLastError(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 pDIB
)
250 PBITMAPINFO pBmi
, pConvertedBmi
= NULL
;
252 UINT offset
= 0; /* Stupid compiler */
254 pBmi
= (BITMAPINFO
*)pDIB
;
256 //hdc = UserGetDCEx(NULL, NULL, DCX_USESTYLE);
257 hdc
= UserGetDCEx(ClipboardWindow
, NULL
, DCX_USESTYLE
);
262 ProbeForRead(&pBmi
->bmiHeader
.biSize
, sizeof(DWORD
), 1);
263 ProbeForRead(pBmi
, pBmi
->bmiHeader
.biSize
, 1);
264 ProbeForRead(pBmi
, DIB_BitmapInfoSize(pBmi
, DIB_RGB_COLORS
), 1);
265 pConvertedBmi
= DIB_ConvertBitmapInfo(pBmi
, DIB_RGB_COLORS
);
268 Status
= STATUS_INVALID_PARAMETER
;
272 offset
= DIB_BitmapInfoSize((BITMAPINFO
*)pBmi
, DIB_RGB_COLORS
);
273 ProbeForRead(pDIB
+ offset
, pConvertedBmi
->bmiHeader
.biSizeImage
, 1);
276 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
278 Status
= _SEH2_GetExceptionCode();
282 if(!NT_SUCCESS(Status
))
284 UserReleaseDC(ClipboardWindow
, hdc
, FALSE
);
288 hbitmap
= GreCreateDIBitmapInternal(hdc
,
289 pConvertedBmi
->bmiHeader
.biWidth
,
290 pConvertedBmi
->bmiHeader
.biHeight
,
297 //UserReleaseDC(NULL, hdc, FALSE);
298 UserReleaseDC(ClipboardWindow
, hdc
, FALSE
);
300 DIB_FreeConvertedBitmapInfo(pConvertedBmi
, pBmi
);
306 canSinthesize(UINT format
)
313 case CF_METAFILEPICT
:
320 /* returns the size of the sinthesized data */
322 synthesizeData(UINT format
)
326 synthesizedData
= NULL
;
327 synthesizedDataSize
= 0;
329 if (!canSinthesize(format
))
341 case CF_METAFILEPICT
:
353 freeSynthesizedData(VOID
)
355 ExFreePool(synthesizedData
);
358 /*==============================================================*/
361 intIsClipboardOpenByMe(VOID
)
363 /* check if we open the clipboard */
364 if (ClipboardThread
&& ClipboardThread
== PsGetCurrentThreadWin32Thread())
366 /* yes, we got a thread and its the same that opens the clipboard */
370 /* will fail if not thread (closed) or not open by me*/
374 /* IntClipboardFreeWindow it's called when a window was destroyed */
376 IntClipboardFreeWindow(PWND window
)
378 /* called from co_UserFreeWindow in window.c */
379 /* check if clipboard is not locked by this window, if yes, unlock it */
380 if (ClipboardThread
== PsGetCurrentThreadWin32Thread())
382 /* the window that opens the clipboard was destroyed */
383 ClipboardThread
= NULL
;
384 ClipboardWindow
= NULL
;
385 //TODO: free clipboard
387 if (window
== ClipboardOwnerWindow
)
389 /* the owner window was destroyed */
390 ClipboardOwnerWindow
= NULL
;
391 ClipboardOwnerThread
= NULL
;
393 /* remove window from window chain */
394 if (IntIsWindowInChain(window
))
396 PCLIPBOARDCHAINELEMENT w
= IntRemoveWindowFromChain(window
);
405 NtUserOpenClipboard(HWND hWnd
, DWORD Unknown1
)
411 UserEnterExclusive();
413 sendDrawClipboardMsg
= FALSE
;
414 recentlySetClipboard
= FALSE
;
418 /* clipboard is already open */
419 if (ClipboardThread
== PsGetCurrentThreadWin32Thread())
421 if (ClipboardOwnerWindow
)
423 if (ClipboardOwnerWindow
->head
.h
== hWnd
)
442 Window
= UserGetWindowObject(hWnd
);
446 ClipboardWindow
= Window
;
447 ClipboardThread
= PsGetCurrentThreadWin32Thread();
452 ClipboardWindow
= NULL
;
453 ClipboardThread
= NULL
;
454 ClipboardOwnerWindow
= NULL
;
455 ClipboardOwnerThread
= NULL
;
460 ClipboardWindow
= NULL
;
461 ClipboardThread
= PsGetCurrentThreadWin32Thread();
472 NtUserCloseClipboard(VOID
)
476 UserEnterExclusive();
478 if (intIsClipboardOpenByMe())
480 ClipboardWindow
= NULL
;
481 ClipboardThread
= NULL
;
486 EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN
);
489 recentlySetClipboard
= FALSE
;
493 if (sendDrawClipboardMsg
&& WindowsChain
)
495 /* only send message to the first window in the chain, then they'll do the chain */
496 /* commented because it makes a crash in co_MsqSendMessage
497 ASSERT(WindowsChain->window);
498 ASSERT(WindowsChain->window->hSelf);
499 DPRINT1("Clipboard: sending WM_DRAWCLIPBOARD to %p\n", WindowsChain->window->hSelf);
500 co_IntSendMessage(WindowsChain->window->hSelf, WM_DRAWCLIPBOARD, 0, 0);
508 NtUserGetOpenClipboardWindow(VOID
)
516 ret
= ClipboardWindow
->head
.h
;
525 NtUserChangeClipboardChain(HWND hWndRemove
, HWND hWndNewNext
)
528 PCLIPBOARDCHAINELEMENT w
= NULL
;
530 UserEnterExclusive();
532 removeWindow
= UserGetWindowObject(hWndRemove
);
536 if ((ret
= !!IntIsWindowInChain(removeWindow
)))
538 w
= IntRemoveWindowFromChain(removeWindow
);
546 if (ret
&& WindowsChain
)
548 // only send message to the first window in the chain,
549 // then they do the chain
551 /* WindowsChain->window may be NULL */
552 LPARAM lparam
= WindowsChain
->window
== NULL
? 0 : (LPARAM
)WindowsChain
->window
->head
.h
;
553 DPRINT1("Message: WM_CHANGECBCHAIN to %p", WindowsChain
->window
->head
.h
);
554 co_IntSendMessage(WindowsChain
->window
->head
.h
, WM_CHANGECBCHAIN
, (WPARAM
)hWndRemove
, lparam
);
563 NtUserCountClipboardFormats(VOID
)
569 ret
= IntCountClipboardFormats();
576 NtUserEmptyClipboard(VOID
)
580 UserEnterExclusive();
582 if (intIsClipboardOpenByMe())
586 IntEmptyClipboardData();
589 ClipboardOwnerWindow
= ClipboardWindow
;
590 ClipboardOwnerThread
= ClipboardThread
;
592 IntIncrementSequenceNumber();
598 EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN
);
601 if (ret
&& ClipboardOwnerWindow
)
603 DPRINT("Clipboard: WM_DESTROYCLIPBOARD to %p", ClipboardOwnerWindow
->head
.h
);
604 co_IntSendMessageNoWait( ClipboardOwnerWindow
->head
.h
, WM_DESTROYCLIPBOARD
, 0, 0);
613 NtUserGetClipboardData(UINT uFormat
, PVOID pBuffer
)
619 if (intIsClipboardOpenByMe())
621 /* when Unknown1 is zero, we returns to user32 the data size */
624 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
628 /* format exists in clipboard */
629 if (data
->size
== DATA_DELAYED_RENDER
)
631 /* tell owner what data needs to be rendered */
632 if (ClipboardOwnerWindow
)
634 ASSERT(ClipboardOwnerWindow
->head
.h
);
635 co_IntSendMessage(ClipboardOwnerWindow
->head
.h
, WM_RENDERFORMAT
, (WPARAM
)uFormat
, 0);
636 data
= intIsFormatAvailable(uFormat
);
638 ret
= (HANDLE
)(ULONG_PTR
)data
->size
;
643 if (data
->size
== DATA_SYNTHESIZED_RENDER
)
645 data
->size
= synthesizeData(uFormat
);
649 ret
= (HANDLE
)(ULONG_PTR
)data
->size
;
653 /* there is no data in this format */
654 //ret = (HANDLE)FALSE;
659 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
663 if (data
->size
== DATA_DELAYED_RENDER
)
665 // we rendered it in 1st call of getclipboard data
669 if (data
->size
== DATA_SYNTHESIZED_RENDER
)
671 if (uFormat
== CF_BITMAP
)
673 /* BITMAP & METAFILEs returns a GDI handle */
674 PCLIPBOARDELEMENT data
= intIsFormatAvailable(CF_DIB
);
677 ret
= renderBITMAPfromDIB(data
->hData
);
682 ret
= (HANDLE
)pBuffer
;
686 ProbeForWrite(pBuffer
, synthesizedDataSize
, 1);
687 memcpy(pBuffer
, (PCHAR
)synthesizedData
, synthesizedDataSize
);
689 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
695 freeSynthesizedData();
700 ret
= (HANDLE
)pBuffer
;
704 ProbeForWrite(pBuffer
, data
->size
, 1);
705 memcpy(pBuffer
, (PCHAR
)data
->hData
, data
->size
);
707 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
721 EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN
);
730 NtUserGetClipboardFormatName(UINT format
, PUNICODE_STRING FormatName
,
733 UNICODE_STRING sFormatName
;
736 /* if the format is built-in we fail */
739 /* registetrated formats are >= 0xc000 */
743 if((cchMaxCount
< 1) || !FormatName
)
745 EngSetLastError(ERROR_INVALID_PARAMETER
);
751 ProbeForWriteUnicodeString(FormatName
);
752 sFormatName
= *(volatile UNICODE_STRING
*)FormatName
;
753 ProbeForWrite(sFormatName
.Buffer
, sFormatName
.MaximumLength
, 1);
755 ret
= IntGetAtomName((RTL_ATOM
)format
, sFormatName
.Buffer
, cchMaxCount
* sizeof(WCHAR
));
759 ret
= ret
/ sizeof(WCHAR
);
760 sFormatName
.Length
= ret
;
767 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
769 SetLastNtError(_SEH2_GetExceptionCode());
777 NtUserGetClipboardOwner(VOID
)
783 if (ClipboardOwnerWindow
)
785 ret
= ClipboardOwnerWindow
->head
.h
;
794 NtUserGetClipboardViewer(VOID
)
802 ret
= WindowsChain
->window
->head
.h
;
811 NtUserGetPriorityClipboardFormat(UINT
*paFormatPriorityList
, INT cFormats
)
817 UserEnterExclusive();
821 if (IntCountClipboardFormats() == 0)
827 ProbeForRead(paFormatPriorityList
, cFormats
, sizeof(UINT
));
829 priorityList
= paFormatPriorityList
;
833 for (i
= 0; i
< cFormats
; i
++)
835 if (intIsFormatAvailable(priorityList
[i
]))
837 ret
= priorityList
[i
];
844 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
846 SetLastNtError(_SEH2_GetExceptionCode());
857 NtUserIsClipboardFormatAvailable(UINT format
)
863 ret
= (intIsFormatAvailable(format
) != NULL
);
873 NtUserSetClipboardData(UINT uFormat
, HANDLE hMem
, DWORD size
)
875 HANDLE hCBData
= NULL
;
876 UNICODE_STRING unicodeString
;
877 OEM_STRING oemString
;
878 ANSI_STRING ansiString
;
880 UserEnterExclusive();
882 /* to place data here the we need to be the owner */
883 if (ClipboardOwnerThread
== PsGetCurrentThreadWin32Thread())
885 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
889 if (data
->size
== DATA_DELAYED_RENDER
)
891 intRemoveFormatedData(uFormat
);
895 // we already have this format on clipboard
904 ProbeForRead(hMem
, size
, 1);
906 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
908 SetLastNtError(_SEH2_GetExceptionCode());
909 _SEH2_YIELD(goto exit_setCB
);
913 if (intIsClipboardOpenByMe())
915 delayedRender
= FALSE
;
918 if (!canSinthesize(uFormat
))
920 hCBData
= ExAllocatePoolWithTag(PagedPool
, size
, USERTAG_CLIPBOARD
);
921 memcpy(hCBData
, hMem
, size
);
922 intAddFormatedData(uFormat
, hCBData
, size
);
923 DPRINT1("Data stored\n");
926 sendDrawClipboardMsg
= TRUE
;
927 recentlySetClipboard
= TRUE
;
928 lastEnumClipboardFormats
= uFormat
;
935 //TODO : sinthesize CF_UNICODETEXT & CF_OEMTEXT
936 // CF_TEXT -> CF_UNICODETEXT
937 ansiString
.Buffer
= hCBData
;
938 ansiString
.Length
= size
;
939 RtlAnsiStringToUnicodeString(&unicodeString
, &ansiString
, TRUE
);
940 intAddFormatedData(CF_UNICODETEXT
, unicodeString
.Buffer
, unicodeString
.Length
* sizeof(WCHAR
));
941 // CF_TEXT -> CF_OEMTEXT
942 RtlUnicodeStringToOemString(&oemString
, &unicodeString
, TRUE
);
943 intAddFormatedData(CF_OEMTEXT
, oemString
.Buffer
, oemString
.Length
);
944 //HKCU\Control Panel\International\Locale
945 //intAddFormatedData(CF_LOCALE, oemString.Buffer, oemString.Length);
950 //TODO : sinthesize CF_TEXT & CF_OEMTEXT
951 //CF_UNICODETEXT -> CF_TEXT
952 unicodeString
.Buffer
= hCBData
;
953 unicodeString
.Length
= size
;
954 RtlUnicodeStringToAnsiString(&ansiString
, &unicodeString
, TRUE
);
955 intAddFormatedData(CF_TEXT
, ansiString
.Buffer
, ansiString
.Length
);
956 //CF_UNICODETEXT -> CF_OEMTEXT
957 RtlUnicodeStringToOemString(&oemString
, &unicodeString
, TRUE
);
958 intAddFormatedData(CF_OEMTEXT
, oemString
.Buffer
, oemString
.Length
);
963 //TODO : sinthesize CF_TEXT & CF_UNICODETEXT
964 //CF_OEMTEXT -> CF_UNICODETEXT
965 oemString
.Buffer
= hCBData
;
966 oemString
.Length
= size
;
967 RtlOemStringToUnicodeString(&unicodeString
, &oemString
, TRUE
);
968 intAddFormatedData(CF_UNICODETEXT
, unicodeString
.Buffer
, unicodeString
.Length
* sizeof(WCHAR
));
969 //CF_OEMTEXT -> CF_TEXT
970 RtlUnicodeStringToAnsiString(&ansiString
, &unicodeString
, TRUE
);
971 intAddFormatedData(CF_TEXT
, ansiString
.Buffer
, ansiString
.Length
);
976 // we need to render the DIB or DIBV5 format as soon as possible
977 // because pallette information may change
984 hdc
= UserGetDCEx(NULL
, NULL
, DCX_USESTYLE
);
987 psurf
= SURFACE_ShareLockSurface(hMem
);
988 BITMAP_GetObject(psurf
, sizeof(BITMAP
), (PVOID
)&bm
);
991 SURFACE_ShareUnlockSurface(psurf
);
994 bi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
995 bi
.bmiHeader
.biWidth
= bm
.bmWidth
;
996 bi
.bmiHeader
.biHeight
= bm
.bmHeight
;
997 bi
.bmiHeader
.biPlanes
= 1;
998 bi
.bmiHeader
.biBitCount
= bm
.bmPlanes
* bm
.bmBitsPixel
;
999 bi
.bmiHeader
.biCompression
= BI_RGB
;
1000 bi
.bmiHeader
.biSizeImage
= 0;
1001 bi
.bmiHeader
.biXPelsPerMeter
= 0;
1002 bi
.bmiHeader
.biYPelsPerMeter
= 0;
1003 bi
.bmiHeader
.biClrUsed
= 0;
1005 NtGdiGetDIBitsInternal(hdc
, hMem
, 0, bm
.bmHeight
, NULL
, &bi
, DIB_RGB_COLORS
, 0, 0);
1007 size
= bi
.bmiHeader
.biSizeImage
+ sizeof(BITMAPINFOHEADER
);
1009 hCBData
= ExAllocatePoolWithTag(PagedPool
, size
, USERTAG_CLIPBOARD
);
1010 memcpy(hCBData
, &bi
, sizeof(BITMAPINFOHEADER
));
1012 NtGdiGetDIBitsInternal(hdc
, hMem
, 0, bm
.bmHeight
, (LPBYTE
)hCBData
+ sizeof(BITMAPINFOHEADER
), &bi
, DIB_RGB_COLORS
, 0, 0);
1014 UserReleaseDC(NULL
, hdc
, FALSE
);
1016 intAddFormatedData(CF_DIB
, hCBData
, size
);
1017 intAddFormatedData(CF_BITMAP
, 0, DATA_SYNTHESIZED_RENDER
);
1018 // intAddFormatedData(CF_DIBV5, hCBData, size);
1024 intAddFormatedData(CF_BITMAP
, 0, DATA_SYNTHESIZED_RENDER
);
1025 // intAddFormatedData(CF_DIBV5, hCBData, size);
1027 // intAddFormatedData(CF_PALETTE, hCBData, size);
1031 // intAddFormatedData(CF_BITMAP, hCBData, size);
1032 // intAddFormatedData(CF_PALETTE, hCBData, size);
1033 // intAddFormatedData(CF_DIB, hCBData, size);
1035 case CF_ENHMETAFILE
:
1036 // intAddFormatedData(CF_METAFILEPICT, hCBData, size);
1038 case CF_METAFILEPICT
:
1039 // intAddFormatedData(CF_ENHMETAFILE, hCBData, size);
1046 // the window provides data in the specified format
1047 delayedRender
= TRUE
;
1048 sendDrawClipboardMsg
= TRUE
;
1049 intAddFormatedData(uFormat
, NULL
, 0);
1050 DPRINT1("SetClipboardData delayed format: %d\n", uFormat
);
1064 NtUserSetClipboardViewer(HWND hWndNewViewer
)
1067 PCLIPBOARDCHAINELEMENT newWC
= NULL
;
1070 UserEnterExclusive();
1072 window
= UserGetWindowObject(hWndNewViewer
);
1076 if ((newWC
= IntAddWindowToChain(window
)))
1080 // newWC->next may be NULL if we are the first window in the chain
1083 // return the next HWND available window in the chain
1084 ret
= newWC
->next
->window
->head
.h
;
1096 IntEnumClipboardFormats(UINT uFormat
)
1100 if (intIsClipboardOpenByMe())
1104 if (recentlySetClipboard
)
1106 ret
= lastEnumClipboardFormats
;
1110 /* return the first available format */
1113 ret
= ClipboardData
->format
;
1119 if (recentlySetClipboard
)
1125 /* querying nextt available format */
1126 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
1132 ret
= data
->next
->format
;
1136 /* reached the end */
1146 EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN
);
1152 // This number is incremented whenever the contents of the clipboard change
1153 // or the clipboard is emptied.
1154 // If clipboard rendering is delayed,
1155 // the sequence number is not incremented until the changes are rendered.
1157 IntIncrementSequenceNumber(VOID
)
1160 PWINSTATION_OBJECT WinStaObj
;
1162 pti
= PsGetCurrentThreadWin32Thread();
1163 WinStaObj
= pti
->rpdesk
->rpwinstaParent
;
1165 WinStaObj
->Clipboard
->ClipboardSequenceNumber
++;
1169 NtUserGetClipboardSequenceNumber(VOID
)
1171 //windowstation sequence number
1172 //if no WINSTA_ACCESSCLIPBOARD access to the window station,
1173 //the function returns zero.
1177 PWINSTATION_OBJECT WinStaObj
;
1180 WinSta
= UserGetProcessWindowStation();
1182 Status
= IntValidateWindowStationHandle(WinSta
, KernelMode
, WINSTA_ACCESSCLIPBOARD
, &WinStaObj
);
1184 if (!NT_SUCCESS(Status
))
1186 DPRINT1("No WINSTA_ACCESSCLIPBOARD access\n");
1187 SetLastNtError(Status
);
1191 sn
= WinStaObj
->ClipboardSequenceNumber
;
1193 ObDereferenceObject(WinStaObj
);
1196 //sn = ClipboardSequenceNumber;