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>
12 DBG_DEFAULT_CHANNEL(UserClipbrd
);
14 #define DATA_DELAYED_RENDER 0
15 #define DATA_SYNTHESIZED_RENDER -1
17 PTHREADINFO ClipboardThread
;
18 PTHREADINFO ClipboardOwnerThread
;
20 PWND ClipboardViewerWindow
;
21 PWND ClipboardOwnerWindow
;
22 BOOL sendDrawClipboardMsg
;
23 BOOL recentlySetClipboard
;
25 UINT lastEnumClipboardFormats
;
26 DWORD ClipboardSequenceNumber
= 0;
28 PCLIPBOARDCHAINELEMENT WindowsChain
= NULL
;
29 PCLIPBOARDELEMENT ClipboardData
= NULL
;
31 PCHAR synthesizedData
;
32 DWORD synthesizedDataSize
;
35 /*==============================================================*/
37 /* return the pointer to the prev window of the finded window,
38 if NULL does not exists in the chain */
39 PCLIPBOARDCHAINELEMENT FASTCALL
40 IntIsWindowInChain(PWND window
)
42 PCLIPBOARDCHAINELEMENT wce
= WindowsChain
;
46 if (wce
->window
== window
)
56 VOID FASTCALL
printChain(VOID
)
59 PCLIPBOARDCHAINELEMENT wce2
= WindowsChain
;
62 ERR("chain: %p\n", wce2
->window
->head
.h
);
67 /* the new window always have to be the first in the chain */
68 PCLIPBOARDCHAINELEMENT FASTCALL
69 IntAddWindowToChain(PWND window
)
71 PCLIPBOARDCHAINELEMENT wce
= NULL
;
73 if (!IntIsWindowInChain(window
))
77 wce
= ExAllocatePoolWithTag(PagedPool
, sizeof(CLIPBOARDCHAINELEMENT
), USERTAG_CLIPBOARD
);
80 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
85 wce
->next
= WindowsChain
;
93 /* return the next window to beremoved later */
97 PCLIPBOARDCHAINELEMENT FASTCALL
98 IntRemoveWindowFromChain(PWND window
)
100 PCLIPBOARDCHAINELEMENT wce
= WindowsChain
;
101 PCLIPBOARDCHAINELEMENT
*link
= &WindowsChain
;
103 if (IntIsWindowInChain(window
))
107 if (wce
->window
== window
)
128 /*==============================================================*/
129 /* if format exists, returns a non zero value (pointing to format object) */
130 PCLIPBOARDELEMENT FASTCALL
131 intIsFormatAvailable(UINT format
)
133 PCLIPBOARDELEMENT ret
= NULL
;
134 PCLIPBOARDELEMENT ce
= ClipboardData
;
138 if (ce
->format
== format
)
148 /* counts how many distinct format were are in the clipboard */
150 IntCountClipboardFormats(VOID
)
153 PCLIPBOARDELEMENT ce
= ClipboardData
;
163 /* adds a new format and data to the clipboard */
164 PCLIPBOARDELEMENT FASTCALL
165 intAddFormatedData(UINT format
, HANDLE hData
, DWORD size
)
167 PCLIPBOARDELEMENT ce
= NULL
;
169 ce
= ExAllocatePoolWithTag(PagedPool
, sizeof(CLIPBOARDELEMENT
), USERTAG_CLIPBOARD
);
172 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
179 ce
->next
= ClipboardData
;
183 IntIncrementSequenceNumber();
189 /* removes a format and its data from the clipboard */
191 intRemoveFormatedData(UINT format
)
194 PCLIPBOARDELEMENT ce
= ClipboardData
;
195 PCLIPBOARDELEMENT
*link
= &ClipboardData
;
197 if (intIsFormatAvailable(format
))
201 if (ce
->format
== format
)
213 ExFreePool(ce
->hData
);
223 IntEmptyClipboardData(VOID
)
225 PCLIPBOARDELEMENT ce
= ClipboardData
;
226 PCLIPBOARDELEMENT tmp
;
233 ExFreePool(ce
->hData
);
239 ClipboardData
= NULL
;
242 /*==============================================================*/
245 renderBITMAPfromDIB(LPBYTE pDIB
)
249 PBITMAPINFO pBmi
, pConvertedBmi
= NULL
;
251 UINT offset
= 0; /* Stupid compiler */
253 pBmi
= (BITMAPINFO
*)pDIB
;
255 //hdc = UserGetDCEx(NULL, NULL, DCX_USESTYLE);
256 hdc
= UserGetDCEx(ClipboardWindow
, NULL
, DCX_USESTYLE
);
261 ProbeForRead(&pBmi
->bmiHeader
.biSize
, sizeof(DWORD
), 1);
262 ProbeForRead(pBmi
, pBmi
->bmiHeader
.biSize
, 1);
263 ProbeForRead(pBmi
, DIB_BitmapInfoSize(pBmi
, DIB_RGB_COLORS
), 1);
264 pConvertedBmi
= DIB_ConvertBitmapInfo(pBmi
, DIB_RGB_COLORS
);
267 Status
= STATUS_INVALID_PARAMETER
;
271 offset
= DIB_BitmapInfoSize((BITMAPINFO
*)pBmi
, DIB_RGB_COLORS
);
272 ProbeForRead(pDIB
+ offset
, pConvertedBmi
->bmiHeader
.biSizeImage
, 1);
275 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
277 Status
= _SEH2_GetExceptionCode();
281 if(!NT_SUCCESS(Status
))
283 UserReleaseDC(ClipboardWindow
, hdc
, FALSE
);
287 hbitmap
= GreCreateDIBitmapInternal(hdc
,
288 pConvertedBmi
->bmiHeader
.biWidth
,
289 pConvertedBmi
->bmiHeader
.biHeight
,
296 //UserReleaseDC(NULL, hdc, FALSE);
297 UserReleaseDC(ClipboardWindow
, hdc
, FALSE
);
299 DIB_FreeConvertedBitmapInfo(pConvertedBmi
, pBmi
);
305 canSinthesize(UINT format
)
312 case CF_METAFILEPICT
:
319 /* returns the size of the sinthesized data */
321 synthesizeData(UINT format
)
325 synthesizedData
= NULL
;
326 synthesizedDataSize
= 0;
328 if (!canSinthesize(format
))
340 case CF_METAFILEPICT
:
352 freeSynthesizedData(VOID
)
354 ExFreePool(synthesizedData
);
357 /*==============================================================*/
360 intIsClipboardOpenByMe(VOID
)
362 /* check if we open the clipboard */
363 if (ClipboardThread
&& ClipboardThread
== PsGetCurrentThreadWin32Thread())
365 /* yes, we got a thread and its the same that opens the clipboard */
369 /* will fail if not thread (closed) or not open by me*/
373 /* IntClipboardFreeWindow it's called when a window was destroyed */
375 IntClipboardFreeWindow(PWND window
)
377 /* called from co_UserFreeWindow in window.c */
378 /* check if clipboard is not locked by this window, if yes, unlock it */
379 if (ClipboardThread
== PsGetCurrentThreadWin32Thread())
381 /* the window that opens the clipboard was destroyed */
382 ClipboardThread
= NULL
;
383 ClipboardWindow
= NULL
;
384 //TODO: free clipboard
386 if (window
== ClipboardOwnerWindow
)
388 /* the owner window was destroyed */
389 ClipboardOwnerWindow
= NULL
;
390 ClipboardOwnerThread
= NULL
;
392 /* remove window from window chain */
393 if (IntIsWindowInChain(window
))
395 PCLIPBOARDCHAINELEMENT w
= IntRemoveWindowFromChain(window
);
404 NtUserOpenClipboard(HWND hWnd
, DWORD Unknown1
)
410 UserEnterExclusive();
412 sendDrawClipboardMsg
= FALSE
;
413 recentlySetClipboard
= FALSE
;
417 /* clipboard is already open */
418 if (ClipboardThread
== PsGetCurrentThreadWin32Thread())
420 if (ClipboardOwnerWindow
)
422 if (ClipboardOwnerWindow
->head
.h
== hWnd
)
441 Window
= UserGetWindowObject(hWnd
);
445 ClipboardWindow
= Window
;
446 ClipboardThread
= PsGetCurrentThreadWin32Thread();
451 ClipboardWindow
= NULL
;
452 ClipboardThread
= NULL
;
453 ClipboardOwnerWindow
= NULL
;
454 ClipboardOwnerThread
= NULL
;
459 ClipboardWindow
= NULL
;
460 ClipboardThread
= PsGetCurrentThreadWin32Thread();
471 NtUserCloseClipboard(VOID
)
475 UserEnterExclusive();
477 if (intIsClipboardOpenByMe())
479 ClipboardWindow
= NULL
;
480 ClipboardThread
= NULL
;
485 EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN
);
488 recentlySetClipboard
= FALSE
;
492 if (sendDrawClipboardMsg
&& WindowsChain
)
494 /* only send message to the first window in the chain, then they'll do the chain */
495 /* commented because it makes a crash in co_MsqSendMessage
496 ASSERT(WindowsChain->window);
497 ASSERT(WindowsChain->window->hSelf);
498 ERR("Clipboard: sending WM_DRAWCLIPBOARD to %p\n", WindowsChain->window->hSelf);
499 co_IntSendMessage(WindowsChain->window->hSelf, WM_DRAWCLIPBOARD, 0, 0);
507 NtUserGetOpenClipboardWindow(VOID
)
515 ret
= ClipboardWindow
->head
.h
;
524 NtUserChangeClipboardChain(HWND hWndRemove
, HWND hWndNewNext
)
527 PCLIPBOARDCHAINELEMENT w
= NULL
;
529 UserEnterExclusive();
531 removeWindow
= UserGetWindowObject(hWndRemove
);
535 if ((ret
= !!IntIsWindowInChain(removeWindow
)))
537 w
= IntRemoveWindowFromChain(removeWindow
);
545 if (ret
&& WindowsChain
)
547 // only send message to the first window in the chain,
548 // then they do the chain
550 /* WindowsChain->window may be NULL */
551 LPARAM lparam
= WindowsChain
->window
== NULL
? 0 : (LPARAM
)WindowsChain
->window
->head
.h
;
552 ERR("Message: WM_CHANGECBCHAIN to %p", WindowsChain
->window
->head
.h
);
553 co_IntSendMessage(WindowsChain
->window
->head
.h
, WM_CHANGECBCHAIN
, (WPARAM
)hWndRemove
, lparam
);
562 NtUserCountClipboardFormats(VOID
)
568 ret
= IntCountClipboardFormats();
575 NtUserEmptyClipboard(VOID
)
579 UserEnterExclusive();
581 if (intIsClipboardOpenByMe())
585 IntEmptyClipboardData();
588 ClipboardOwnerWindow
= ClipboardWindow
;
589 ClipboardOwnerThread
= ClipboardThread
;
591 IntIncrementSequenceNumber();
597 EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN
);
600 if (ret
&& ClipboardOwnerWindow
)
602 TRACE("Clipboard: WM_DESTROYCLIPBOARD to %p", ClipboardOwnerWindow
->head
.h
);
603 co_IntSendMessageNoWait( ClipboardOwnerWindow
->head
.h
, WM_DESTROYCLIPBOARD
, 0, 0);
612 NtUserGetClipboardData(UINT uFormat
, PVOID pBuffer
)
618 if (intIsClipboardOpenByMe())
620 /* when Unknown1 is zero, we returns to user32 the data size */
623 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
627 /* format exists in clipboard */
628 if (data
->size
== DATA_DELAYED_RENDER
)
630 /* tell owner what data needs to be rendered */
631 if (ClipboardOwnerWindow
)
633 ASSERT(ClipboardOwnerWindow
->head
.h
);
634 co_IntSendMessage(ClipboardOwnerWindow
->head
.h
, WM_RENDERFORMAT
, (WPARAM
)uFormat
, 0);
635 data
= intIsFormatAvailable(uFormat
);
637 ret
= (HANDLE
)(ULONG_PTR
)data
->size
;
642 if (data
->size
== DATA_SYNTHESIZED_RENDER
)
644 data
->size
= synthesizeData(uFormat
);
648 ret
= (HANDLE
)(ULONG_PTR
)data
->size
;
652 /* there is no data in this format */
653 //ret = (HANDLE)FALSE;
658 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
662 if (data
->size
== DATA_DELAYED_RENDER
)
664 // we rendered it in 1st call of getclipboard data
668 if (data
->size
== DATA_SYNTHESIZED_RENDER
)
670 if (uFormat
== CF_BITMAP
)
672 /* BITMAP & METAFILEs returns a GDI handle */
673 PCLIPBOARDELEMENT data
= intIsFormatAvailable(CF_DIB
);
676 ret
= renderBITMAPfromDIB(data
->hData
);
681 ret
= (HANDLE
)pBuffer
;
685 ProbeForWrite(pBuffer
, synthesizedDataSize
, 1);
686 memcpy(pBuffer
, (PCHAR
)synthesizedData
, synthesizedDataSize
);
688 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
694 freeSynthesizedData();
699 ret
= (HANDLE
)pBuffer
;
703 ProbeForWrite(pBuffer
, data
->size
, 1);
704 memcpy(pBuffer
, (PCHAR
)data
->hData
, data
->size
);
706 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
720 EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN
);
729 NtUserGetClipboardFormatName(UINT format
, PUNICODE_STRING FormatName
,
732 UNICODE_STRING sFormatName
;
735 /* if the format is built-in we fail */
738 /* registetrated formats are >= 0xc000 */
742 if((cchMaxCount
< 1) || !FormatName
)
744 EngSetLastError(ERROR_INVALID_PARAMETER
);
750 ProbeForWriteUnicodeString(FormatName
);
751 sFormatName
= *(volatile UNICODE_STRING
*)FormatName
;
752 ProbeForWrite(sFormatName
.Buffer
, sFormatName
.MaximumLength
, 1);
754 ret
= IntGetAtomName((RTL_ATOM
)format
, sFormatName
.Buffer
, cchMaxCount
* sizeof(WCHAR
));
758 ret
= ret
/ sizeof(WCHAR
);
759 sFormatName
.Length
= ret
;
766 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
768 SetLastNtError(_SEH2_GetExceptionCode());
776 NtUserGetClipboardOwner(VOID
)
782 if (ClipboardOwnerWindow
)
784 ret
= ClipboardOwnerWindow
->head
.h
;
793 NtUserGetClipboardViewer(VOID
)
801 ret
= WindowsChain
->window
->head
.h
;
810 NtUserGetPriorityClipboardFormat(UINT
*paFormatPriorityList
, INT cFormats
)
816 UserEnterExclusive();
820 if (IntCountClipboardFormats() == 0)
826 ProbeForRead(paFormatPriorityList
, cFormats
, sizeof(UINT
));
828 priorityList
= paFormatPriorityList
;
832 for (i
= 0; i
< cFormats
; i
++)
834 if (intIsFormatAvailable(priorityList
[i
]))
836 ret
= priorityList
[i
];
843 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
845 SetLastNtError(_SEH2_GetExceptionCode());
856 NtUserIsClipboardFormatAvailable(UINT format
)
862 ret
= (intIsFormatAvailable(format
) != NULL
);
872 NtUserSetClipboardData(UINT uFormat
, HANDLE hMem
, DWORD size
)
874 HANDLE hCBData
= NULL
;
875 UNICODE_STRING unicodeString
;
876 OEM_STRING oemString
;
877 ANSI_STRING ansiString
;
879 UserEnterExclusive();
881 /* to place data here the we need to be the owner */
882 if (ClipboardOwnerThread
== PsGetCurrentThreadWin32Thread())
884 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
888 if (data
->size
== DATA_DELAYED_RENDER
)
890 intRemoveFormatedData(uFormat
);
894 // we already have this format on clipboard
903 ProbeForRead(hMem
, size
, 1);
905 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
907 SetLastNtError(_SEH2_GetExceptionCode());
908 _SEH2_YIELD(goto exit_setCB
);
912 if (intIsClipboardOpenByMe())
914 delayedRender
= FALSE
;
917 if (!canSinthesize(uFormat
))
919 hCBData
= ExAllocatePoolWithTag(PagedPool
, size
, USERTAG_CLIPBOARD
);
920 memcpy(hCBData
, hMem
, size
);
921 intAddFormatedData(uFormat
, hCBData
, size
);
922 ERR("Data stored\n");
925 sendDrawClipboardMsg
= TRUE
;
926 recentlySetClipboard
= TRUE
;
927 lastEnumClipboardFormats
= uFormat
;
934 //TODO : sinthesize CF_UNICODETEXT & CF_OEMTEXT
935 // CF_TEXT -> CF_UNICODETEXT
936 ansiString
.Buffer
= hCBData
;
937 ansiString
.Length
= size
;
938 RtlAnsiStringToUnicodeString(&unicodeString
, &ansiString
, TRUE
);
939 intAddFormatedData(CF_UNICODETEXT
, unicodeString
.Buffer
, unicodeString
.Length
* sizeof(WCHAR
));
940 // CF_TEXT -> CF_OEMTEXT
941 RtlUnicodeStringToOemString(&oemString
, &unicodeString
, TRUE
);
942 intAddFormatedData(CF_OEMTEXT
, oemString
.Buffer
, oemString
.Length
);
943 //HKCU\Control Panel\International\Locale
944 //intAddFormatedData(CF_LOCALE, oemString.Buffer, oemString.Length);
949 //TODO : sinthesize CF_TEXT & CF_OEMTEXT
950 //CF_UNICODETEXT -> CF_TEXT
951 unicodeString
.Buffer
= hCBData
;
952 unicodeString
.Length
= size
;
953 RtlUnicodeStringToAnsiString(&ansiString
, &unicodeString
, TRUE
);
954 intAddFormatedData(CF_TEXT
, ansiString
.Buffer
, ansiString
.Length
);
955 //CF_UNICODETEXT -> CF_OEMTEXT
956 RtlUnicodeStringToOemString(&oemString
, &unicodeString
, TRUE
);
957 intAddFormatedData(CF_OEMTEXT
, oemString
.Buffer
, oemString
.Length
);
962 //TODO : sinthesize CF_TEXT & CF_UNICODETEXT
963 //CF_OEMTEXT -> CF_UNICODETEXT
964 oemString
.Buffer
= hCBData
;
965 oemString
.Length
= size
;
966 RtlOemStringToUnicodeString(&unicodeString
, &oemString
, TRUE
);
967 intAddFormatedData(CF_UNICODETEXT
, unicodeString
.Buffer
, unicodeString
.Length
* sizeof(WCHAR
));
968 //CF_OEMTEXT -> CF_TEXT
969 RtlUnicodeStringToAnsiString(&ansiString
, &unicodeString
, TRUE
);
970 intAddFormatedData(CF_TEXT
, ansiString
.Buffer
, ansiString
.Length
);
975 // we need to render the DIB or DIBV5 format as soon as possible
976 // because pallette information may change
983 hdc
= UserGetDCEx(NULL
, NULL
, DCX_USESTYLE
);
986 psurf
= SURFACE_ShareLockSurface(hMem
);
987 BITMAP_GetObject(psurf
, sizeof(BITMAP
), (PVOID
)&bm
);
990 SURFACE_ShareUnlockSurface(psurf
);
993 bi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
994 bi
.bmiHeader
.biWidth
= bm
.bmWidth
;
995 bi
.bmiHeader
.biHeight
= bm
.bmHeight
;
996 bi
.bmiHeader
.biPlanes
= 1;
997 bi
.bmiHeader
.biBitCount
= bm
.bmPlanes
* bm
.bmBitsPixel
;
998 bi
.bmiHeader
.biCompression
= BI_RGB
;
999 bi
.bmiHeader
.biSizeImage
= 0;
1000 bi
.bmiHeader
.biXPelsPerMeter
= 0;
1001 bi
.bmiHeader
.biYPelsPerMeter
= 0;
1002 bi
.bmiHeader
.biClrUsed
= 0;
1004 NtGdiGetDIBitsInternal(hdc
, hMem
, 0, bm
.bmHeight
, NULL
, &bi
, DIB_RGB_COLORS
, 0, 0);
1006 size
= bi
.bmiHeader
.biSizeImage
+ sizeof(BITMAPINFOHEADER
);
1008 hCBData
= ExAllocatePoolWithTag(PagedPool
, size
, USERTAG_CLIPBOARD
);
1009 memcpy(hCBData
, &bi
, sizeof(BITMAPINFOHEADER
));
1011 NtGdiGetDIBitsInternal(hdc
, hMem
, 0, bm
.bmHeight
, (LPBYTE
)hCBData
+ sizeof(BITMAPINFOHEADER
), &bi
, DIB_RGB_COLORS
, 0, 0);
1013 UserReleaseDC(NULL
, hdc
, FALSE
);
1015 intAddFormatedData(CF_DIB
, hCBData
, size
);
1016 intAddFormatedData(CF_BITMAP
, 0, DATA_SYNTHESIZED_RENDER
);
1017 // intAddFormatedData(CF_DIBV5, hCBData, size);
1023 intAddFormatedData(CF_BITMAP
, 0, DATA_SYNTHESIZED_RENDER
);
1024 // intAddFormatedData(CF_DIBV5, hCBData, size);
1026 // intAddFormatedData(CF_PALETTE, hCBData, size);
1030 // intAddFormatedData(CF_BITMAP, hCBData, size);
1031 // intAddFormatedData(CF_PALETTE, hCBData, size);
1032 // intAddFormatedData(CF_DIB, hCBData, size);
1034 case CF_ENHMETAFILE
:
1035 // intAddFormatedData(CF_METAFILEPICT, hCBData, size);
1037 case CF_METAFILEPICT
:
1038 // intAddFormatedData(CF_ENHMETAFILE, hCBData, size);
1045 // the window provides data in the specified format
1046 delayedRender
= TRUE
;
1047 sendDrawClipboardMsg
= TRUE
;
1048 intAddFormatedData(uFormat
, NULL
, 0);
1049 ERR("SetClipboardData delayed format: %d\n", uFormat
);
1063 NtUserSetClipboardViewer(HWND hWndNewViewer
)
1066 PCLIPBOARDCHAINELEMENT newWC
= NULL
;
1069 UserEnterExclusive();
1071 window
= UserGetWindowObject(hWndNewViewer
);
1075 if ((newWC
= IntAddWindowToChain(window
)))
1079 // newWC->next may be NULL if we are the first window in the chain
1082 // return the next HWND available window in the chain
1083 ret
= newWC
->next
->window
->head
.h
;
1095 IntEnumClipboardFormats(UINT uFormat
)
1099 if (intIsClipboardOpenByMe())
1103 if (recentlySetClipboard
)
1105 ret
= lastEnumClipboardFormats
;
1109 /* return the first available format */
1112 ret
= ClipboardData
->format
;
1118 if (recentlySetClipboard
)
1124 /* querying nextt available format */
1125 PCLIPBOARDELEMENT data
= intIsFormatAvailable(uFormat
);
1131 ret
= data
->next
->format
;
1135 /* reached the end */
1145 EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN
);
1151 // This number is incremented whenever the contents of the clipboard change
1152 // or the clipboard is emptied.
1153 // If clipboard rendering is delayed,
1154 // the sequence number is not incremented until the changes are rendered.
1156 IntIncrementSequenceNumber(VOID
)
1159 PWINSTATION_OBJECT WinStaObj
;
1161 pti
= PsGetCurrentThreadWin32Thread();
1162 WinStaObj
= pti
->rpdesk
->rpwinstaParent
;
1164 WinStaObj
->Clipboard
->ClipboardSequenceNumber
++;
1168 NtUserGetClipboardSequenceNumber(VOID
)
1170 //windowstation sequence number
1171 //if no WINSTA_ACCESSCLIPBOARD access to the window station,
1172 //the function returns zero.
1176 PWINSTATION_OBJECT WinStaObj
;
1179 WinSta
= UserGetProcessWindowStation();
1181 Status
= IntValidateWindowStationHandle(WinSta
, KernelMode
, WINSTA_ACCESSCLIPBOARD
, &WinStaObj
);
1183 if (!NT_SUCCESS(Status
))
1185 ERR("No WINSTA_ACCESSCLIPBOARD access\n");
1186 SetLastNtError(Status
);
1190 sn
= WinStaObj
->ClipboardSequenceNumber
;
1192 ObDereferenceObject(WinStaObj
);
1195 //sn = ClipboardSequenceNumber;