xref: /reactos/win32ss/user/ntuser/clipboard.c (revision c2c66aff)
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS kernel
4  * PURPOSE:          Clipboard routines
5  * FILE:             win32ss/user/ntuser/clipboard.c
6  * PROGRAMER:        Filip Navara <xnavara@volny.cz>
7  *                   Pablo Borobia <pborobia@gmail.com>
8  *                   Rafal Harabien <rafalh@reactos.org>
9  */
10 
11 #include <win32k.h>
12 DBG_DEFAULT_CHANNEL(UserClipbrd);
13 
14 #define DATA_DELAYED     (HANDLE)0
15 #define DATA_SYNTH_USER  (HANDLE)1
16 #define DATA_SYNTH_KRNL  (HANDLE)2
17 #define IS_DATA_DELAYED(ce)     ((ce)->hData == DATA_DELAYED)
18 #define IS_DATA_SYNTHESIZED(ce) ((ce)->hData == DATA_SYNTH_USER || (ce)->hData == DATA_SYNTH_KRNL)
19 
20 static PWINSTATION_OBJECT FASTCALL
21 IntGetWinStaForCbAccess(VOID)
22 {
23     HWINSTA hWinSta;
24     PWINSTATION_OBJECT pWinStaObj;
25     NTSTATUS Status;
26 
27     hWinSta = UserGetProcessWindowStation();
28     Status = IntValidateWindowStationHandle(hWinSta, UserMode, WINSTA_ACCESSCLIPBOARD, &pWinStaObj, 0);
29     if (!NT_SUCCESS(Status))
30     {
31         ERR("Cannot open winsta\n");
32         SetLastNtError(Status);
33         return NULL;
34     }
35 
36     return pWinStaObj;
37 }
38 
39 /* If format exists, returns a non-null value (pointing to formated object) */
40 static PCLIP FASTCALL
41 IntGetFormatElement(PWINSTATION_OBJECT pWinStaObj, UINT fmt)
42 {
43     DWORD i;
44 
45     for (i = 0; i < pWinStaObj->cNumClipFormats; ++i)
46     {
47         if (pWinStaObj->pClipBase[i].fmt == fmt)
48             return &pWinStaObj->pClipBase[i];
49     }
50 
51     return NULL;
52 }
53 
54 static BOOL FASTCALL
55 IntIsFormatAvailable(PWINSTATION_OBJECT pWinStaObj, UINT fmt)
56 {
57     return IntGetFormatElement(pWinStaObj, fmt) != NULL;
58 }
59 
60 static VOID FASTCALL
61 IntFreeElementData(PCLIP pElement)
62 {
63     if (!IS_DATA_DELAYED(pElement) &&
64         !IS_DATA_SYNTHESIZED(pElement))
65     {
66         if (pElement->fGlobalHandle)
67             UserDeleteObject(pElement->hData, TYPE_CLIPDATA);
68         else if (pElement->fmt == CF_BITMAP || pElement->fmt == CF_PALETTE ||
69                  pElement->fmt == CF_DSPBITMAP)
70         {
71             GreSetObjectOwner(pElement->hData, GDI_OBJ_HMGR_POWNED);
72             GreDeleteObject(pElement->hData);
73         }
74     }
75 }
76 
77 /* Adds a new format and data to the clipboard */
78 static PCLIP NTAPI
79 IntAddFormatedData(PWINSTATION_OBJECT pWinStaObj, UINT fmt, HANDLE hData, BOOLEAN fGlobalHandle, BOOL bEnd)
80 {
81     PCLIP pElement = NULL;
82 
83     /* Use existing entry with specified format */
84     if (!bEnd)
85         pElement = IntGetFormatElement(pWinStaObj, fmt);
86 
87     /* Put new entry at the end if nothing was found */
88     if (!pElement)
89     {
90         /* Allocate bigger clipboard if needed. We could use lists but Windows uses array */
91         if (pWinStaObj->cNumClipFormats % 4 == 0)
92         {
93             PCLIP pNewClip;
94 
95             /* Allocate new clipboard */
96             pNewClip = ExAllocatePoolWithTag(PagedPool,
97                                              (pWinStaObj->cNumClipFormats + 4) * sizeof(CLIP),
98                                              USERTAG_CLIPBOARD);
99             if (!pNewClip)
100             {
101                 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
102                 return NULL;
103             }
104 
105             /* Copy data */
106             memcpy(pNewClip, pWinStaObj->pClipBase, pWinStaObj->cNumClipFormats * sizeof(CLIP));
107 
108             /* Free old clipboard */
109             if (pWinStaObj->pClipBase)
110                 ExFreePoolWithTag(pWinStaObj->pClipBase, USERTAG_CLIPBOARD);
111 
112             /* Update WinSta */
113             pWinStaObj->pClipBase = pNewClip;
114         }
115 
116         /* New element is at the end */
117         pElement = &pWinStaObj->pClipBase[pWinStaObj->cNumClipFormats];
118         pElement->fmt = fmt;
119         pWinStaObj->cNumClipFormats++;
120     }
121     else
122         IntFreeElementData(pElement);
123 
124     pElement->hData = hData;
125     pElement->fGlobalHandle = fGlobalHandle;
126 
127     return pElement;
128 }
129 
130 static BOOL FASTCALL
131 IntIsClipboardOpenByMe(PWINSTATION_OBJECT pWinSta)
132 {
133     /* Check if the current thread has opened the clipboard */
134     return (pWinSta->ptiClipLock &&
135             pWinSta->ptiClipLock == PsGetCurrentThreadWin32Thread());
136 }
137 
138 static VOID NTAPI
139 IntSynthesizeDib(
140     PWINSTATION_OBJECT pWinStaObj,
141     HBITMAP hbm)
142 {
143     HDC hdc;
144     ULONG cjInfoSize, cjDataSize;
145     PCLIPBOARDDATA pClipboardData;
146     HANDLE hMem;
147     INT iResult;
148     struct
149     {
150         BITMAPINFOHEADER bmih;
151         RGBQUAD rgbColors[256];
152     } bmiBuffer;
153     PBITMAPINFO pbmi = (PBITMAPINFO)&bmiBuffer;
154 
155     /* Get the display DC */
156     hdc = UserGetDCEx(NULL, NULL, DCX_USESTYLE);
157     if (!hdc)
158     {
159         return;
160     }
161 
162     /* Get information about the bitmap format */
163     pbmi->bmiHeader.biSize = sizeof(bmiBuffer.bmih);
164     iResult = GreGetDIBitsInternal(hdc,
165                                    hbm,
166                                    0,
167                                    0,
168                                    NULL,
169                                    pbmi,
170                                    DIB_RGB_COLORS,
171                                    0,
172                                    sizeof(bmiBuffer));
173     if (iResult == 0)
174     {
175        goto cleanup;
176     }
177 
178     /* Get the size for a full BITMAPINFO */
179     cjInfoSize = DIB_BitmapInfoSize(pbmi, DIB_RGB_COLORS);
180 
181     /* Calculate the size of the clipboard data, which is a packed DIB */
182     cjDataSize = cjInfoSize + pbmi->bmiHeader.biSizeImage;
183 
184     /* Create the clipboard data */
185     pClipboardData = (PCLIPBOARDDATA)UserCreateObject(gHandleTable,
186                                                       NULL,
187                                                       NULL,
188                                                       &hMem,
189                                                       TYPE_CLIPDATA,
190                                                       sizeof(CLIPBOARDDATA) + cjDataSize);
191     if (!pClipboardData)
192     {
193         goto cleanup;
194     }
195 
196     /* Set the data size */
197     pClipboardData->cbData = cjDataSize;
198 
199     /* Copy the BITMAPINFOHEADER */
200     memcpy(pClipboardData->Data, pbmi, sizeof(BITMAPINFOHEADER));
201 
202     /* Get the bitmap bits and the color table */
203     iResult = GreGetDIBitsInternal(hdc,
204                                    hbm,
205                                    0,
206                                    abs(pbmi->bmiHeader.biHeight),
207                                    (LPBYTE)pClipboardData->Data + cjInfoSize,
208                                    (LPBITMAPINFO)pClipboardData->Data,
209                                    DIB_RGB_COLORS,
210                                    pbmi->bmiHeader.biSizeImage,
211                                    cjInfoSize);
212 
213     /* Add the clipboard data */
214     IntAddFormatedData(pWinStaObj, CF_DIB, hMem, TRUE, TRUE);
215 
216     /* Release the extra reference (UserCreateObject added 2 references) */
217     UserDereferenceObject(pClipboardData);
218 
219 cleanup:
220     UserReleaseDC(NULL, hdc, FALSE);
221 }
222 
223 static VOID WINAPI
224 IntSynthesizeBitmap(PWINSTATION_OBJECT pWinStaObj, PCLIP pBmEl)
225 {
226     HDC hdc = NULL;
227     PBITMAPINFO pBmi, pConvertedBmi = NULL;
228     HBITMAP hBm = NULL;
229     PCLIPBOARDDATA pMemObj;
230     PCLIP pDibEl;
231     ULONG Offset;
232 
233     TRACE("IntSynthesizeBitmap(%p, %p)\n", pWinStaObj, pBmEl);
234 
235     pDibEl = IntGetFormatElement(pWinStaObj, CF_DIB);
236     ASSERT(pDibEl && !IS_DATA_SYNTHESIZED(pDibEl));
237     if (!pDibEl->fGlobalHandle)
238         return;
239 
240     pMemObj = (PCLIPBOARDDATA)UserGetObject(gHandleTable, pDibEl->hData, TYPE_CLIPDATA);
241     if (!pMemObj)
242         return;
243 
244     pBmi = (BITMAPINFO*)pMemObj->Data;
245 
246     if (pMemObj->cbData < sizeof(DWORD) && pMemObj->cbData < pBmi->bmiHeader.biSize)
247         goto cleanup;
248 
249     pConvertedBmi = DIB_ConvertBitmapInfo(pBmi, DIB_RGB_COLORS);
250     if (!pConvertedBmi)
251         goto cleanup;
252 
253     Offset = DIB_BitmapInfoSize(pBmi, DIB_RGB_COLORS);
254 
255     hdc = UserGetDCEx(NULL, NULL, DCX_USESTYLE);
256     if (!hdc)
257         goto cleanup;
258 
259     hBm = GreCreateDIBitmapInternal(hdc,
260                                     pConvertedBmi->bmiHeader.biWidth,
261                                     pConvertedBmi->bmiHeader.biHeight,
262                                     CBM_INIT,
263                                     pMemObj->Data + Offset,
264                                     pConvertedBmi,
265                                     DIB_RGB_COLORS,
266                                     0,
267                                     pMemObj->cbData - Offset,
268                                     0);
269 
270     if (hBm)
271     {
272         GreSetObjectOwner(hBm, GDI_OBJ_HMGR_PUBLIC);
273         pBmEl->hData = hBm;
274     }
275 
276 cleanup:
277     if (hdc)
278         UserReleaseDC(NULL, hdc, FALSE);
279 
280     if (pConvertedBmi)
281         DIB_FreeConvertedBitmapInfo(pConvertedBmi, pBmi, -1);
282 }
283 
284 static VOID NTAPI
285 IntAddSynthesizedFormats(PWINSTATION_OBJECT pWinStaObj)
286 {
287     BOOL bHaveText, bHaveUniText, bHaveOemText, bHaveLocale, bHaveBm, bHaveDib;
288 
289     bHaveText = IntIsFormatAvailable(pWinStaObj, CF_TEXT);
290     bHaveOemText = IntIsFormatAvailable(pWinStaObj, CF_OEMTEXT);
291     bHaveUniText = IntIsFormatAvailable(pWinStaObj, CF_UNICODETEXT);
292     bHaveLocale = IntIsFormatAvailable(pWinStaObj, CF_LOCALE);
293     bHaveBm = IntIsFormatAvailable(pWinStaObj, CF_BITMAP);
294     bHaveDib = IntIsFormatAvailable(pWinStaObj, CF_DIB);
295 
296     /* Add CF_LOCALE format if we have CF_TEXT */
297     if (!bHaveLocale && bHaveText)
298     {
299         PCLIPBOARDDATA pMemObj;
300         HANDLE hMem;
301 
302         pMemObj = (PCLIPBOARDDATA)UserCreateObject(gHandleTable, NULL, NULL, &hMem, TYPE_CLIPDATA,
303                                                    sizeof(CLIPBOARDDATA) + sizeof(LCID));
304         if (pMemObj)
305         {
306             pMemObj->cbData = sizeof(LCID);
307             *((LCID*)pMemObj->Data) = NtCurrentTeb()->CurrentLocale;
308             IntAddFormatedData(pWinStaObj, CF_LOCALE, hMem, TRUE, TRUE);
309 
310             /* Release the extra reference (UserCreateObject added 2 references) */
311             UserDereferenceObject(pMemObj);
312         }
313     }
314 
315     /* Add CF_TEXT. Note: it is synthesized in user32.dll */
316     if (!bHaveText && (bHaveUniText || bHaveOemText))
317         IntAddFormatedData(pWinStaObj, CF_TEXT, DATA_SYNTH_USER, FALSE, TRUE);
318 
319     /* Add CF_OEMTEXT. Note: it is synthesized in user32.dll */
320     if (!bHaveOemText && (bHaveUniText || bHaveText))
321         IntAddFormatedData(pWinStaObj, CF_OEMTEXT, DATA_SYNTH_USER, FALSE, TRUE);
322 
323     /* Add CF_UNICODETEXT. Note: it is synthesized in user32.dll */
324     if (!bHaveUniText && (bHaveText || bHaveOemText))
325         IntAddFormatedData(pWinStaObj, CF_UNICODETEXT, DATA_SYNTH_USER, FALSE, TRUE);
326 
327     /* Add CF_BITMAP. Note: it is synthesized on demand */
328     if (!bHaveBm && bHaveDib)
329         IntAddFormatedData(pWinStaObj, CF_BITMAP, DATA_SYNTH_KRNL, FALSE, TRUE);
330 
331     /* Note: We need to render the DIB or DIBV5 format as soon as possible
332        because pallette information may change */
333     if (!bHaveDib && bHaveBm)
334         IntSynthesizeDib(pWinStaObj, IntGetFormatElement(pWinStaObj, CF_BITMAP)->hData);
335 }
336 
337 VOID NTAPI
338 UserEmptyClipboardData(PWINSTATION_OBJECT pWinSta)
339 {
340     DWORD i;
341     PCLIP pElement;
342 
343     for (i = 0; i < pWinSta->cNumClipFormats; ++i)
344     {
345         pElement = &pWinSta->pClipBase[i];
346         IntFreeElementData(pElement);
347     }
348 
349     if (pWinSta->pClipBase)
350         ExFreePoolWithTag(pWinSta->pClipBase, USERTAG_CLIPBOARD);
351 
352     pWinSta->pClipBase = NULL;
353     pWinSta->cNumClipFormats = 0;
354 }
355 
356 /* UserClipboardRelease is called from IntSendDestroyMsg in window.c */
357 VOID FASTCALL
358 UserClipboardRelease(PWND pWindow)
359 {
360     PWINSTATION_OBJECT pWinStaObj;
361 
362     pWinStaObj = IntGetWinStaForCbAccess();
363     if (!pWinStaObj)
364         return;
365 
366     co_IntSendMessage(pWinStaObj->spwndClipOwner->head.h, WM_RENDERALLFORMATS, 0, 0);
367 
368     /* If the window being destroyed is the current clipboard owner... */
369     if (pWindow == pWinStaObj->spwndClipOwner)
370     {
371         /* ... make it release the clipboard */
372         pWinStaObj->spwndClipOwner = NULL;
373     }
374 
375     if (pWinStaObj->fClipboardChanged)
376     {
377         /* Add synthesized formats - they are rendered later */
378         IntAddSynthesizedFormats(pWinStaObj);
379 
380         /* Notify viewer windows in chain */
381         pWinStaObj->fClipboardChanged = FALSE;
382         if (pWinStaObj->spwndClipViewer)
383         {
384             TRACE("Clipboard: sending WM_DRAWCLIPBOARD to %p\n", pWinStaObj->spwndClipViewer->head.h);
385             // For 32-bit applications this message is sent as a notification
386             co_IntSendMessageNoWait(pWinStaObj->spwndClipViewer->head.h, WM_DRAWCLIPBOARD, 0, 0);
387         }
388     }
389 
390     ObDereferenceObject(pWinStaObj);
391 }
392 
393 /* UserClipboardFreeWindow is called from co_UserFreeWindow in window.c */
394 VOID FASTCALL
395 UserClipboardFreeWindow(PWND pWindow)
396 {
397     PWINSTATION_OBJECT pWinStaObj;
398 
399     pWinStaObj = IntGetWinStaForCbAccess();
400     if (!pWinStaObj)
401         return;
402 
403     if (pWindow == pWinStaObj->spwndClipOwner)
404     {
405         /* The owner window was destroyed */
406         pWinStaObj->spwndClipOwner = NULL;
407     }
408 
409     /* Check if clipboard is not locked by this window, if yes, unlock it */
410     if (pWindow == pWinStaObj->spwndClipOpen)
411     {
412         /* The window that opens the clipboard was destroyed */
413         pWinStaObj->spwndClipOpen = NULL;
414         pWinStaObj->ptiClipLock = NULL;
415     }
416     /* Remove window from window chain */
417     if (pWindow == pWinStaObj->spwndClipViewer)
418         pWinStaObj->spwndClipViewer = NULL;
419 
420     ObDereferenceObject(pWinStaObj);
421 }
422 
423 UINT APIENTRY
424 UserEnumClipboardFormats(UINT fmt)
425 {
426     UINT Ret = 0;
427     PCLIP pElement;
428     PWINSTATION_OBJECT pWinStaObj;
429 
430     pWinStaObj = IntGetWinStaForCbAccess();
431     if (!pWinStaObj)
432         goto cleanup;
433 
434     /* Check if the clipboard has been opened */
435     if (!IntIsClipboardOpenByMe(pWinStaObj))
436     {
437         EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN);
438         goto cleanup;
439     }
440 
441     if (fmt == 0)
442     {
443         /* Return first format */
444         if (pWinStaObj->pClipBase)
445             Ret = pWinStaObj->pClipBase[0].fmt;
446     }
447     else
448     {
449         /* Return next format */
450         pElement = IntGetFormatElement(pWinStaObj, fmt);
451         if (pElement != NULL)
452         {
453             ++pElement;
454             if (pElement < &pWinStaObj->pClipBase[pWinStaObj->cNumClipFormats])
455             {
456                 Ret = pElement->fmt;
457             }
458         }
459     }
460 
461 cleanup:
462     if (pWinStaObj)
463         ObDereferenceObject(pWinStaObj);
464 
465     return Ret;
466 }
467 
468 BOOL NTAPI
469 UserOpenClipboard(HWND hWnd)
470 {
471     PWND pWindow = NULL;
472     BOOL bRet = FALSE;
473     PWINSTATION_OBJECT pWinStaObj = NULL;
474 
475     if (hWnd)
476     {
477         pWindow = UserGetWindowObject(hWnd);
478         if (!pWindow)
479             goto cleanup;
480     }
481 
482     pWinStaObj = IntGetWinStaForCbAccess();
483     if (!pWinStaObj)
484         goto cleanup;
485 
486     /* Check if we already opened the clipboard */
487     if ((pWindow == pWinStaObj->spwndClipOpen) && IntIsClipboardOpenByMe(pWinStaObj))
488     {
489         bRet = TRUE;
490         goto cleanup;
491     }
492 
493     /* If the clipboard was already opened by somebody else, bail out */
494     if ((pWindow != pWinStaObj->spwndClipOpen) && pWinStaObj->ptiClipLock)
495     {
496         ERR("Access denied!\n");
497         EngSetLastError(ERROR_ACCESS_DENIED);
498         goto cleanup;
499     }
500 
501     /* Open the clipboard */
502     pWinStaObj->spwndClipOpen = pWindow;
503     pWinStaObj->ptiClipLock = PsGetCurrentThreadWin32Thread();
504     bRet = TRUE;
505 
506 cleanup:
507     if (pWinStaObj)
508         ObDereferenceObject(pWinStaObj);
509 
510     return bRet;
511 }
512 
513 BOOL APIENTRY
514 NtUserOpenClipboard(HWND hWnd, DWORD Unknown1)
515 {
516     BOOL bRet;
517 
518     UserEnterExclusive();
519     bRet = UserOpenClipboard(hWnd);
520     UserLeave();
521 
522     return bRet;
523 }
524 
525 BOOL NTAPI
526 UserCloseClipboard(VOID)
527 {
528     BOOL bRet = FALSE;
529     PWINSTATION_OBJECT pWinStaObj;
530 
531     pWinStaObj = IntGetWinStaForCbAccess();
532     if (!pWinStaObj)
533         goto cleanup;
534 
535     /* Check if the clipboard has been opened */
536     if (!IntIsClipboardOpenByMe(pWinStaObj))
537     {
538         EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN);
539         goto cleanup;
540     }
541 
542     /* Clipboard is no longer open */
543     pWinStaObj->spwndClipOpen = NULL;
544     pWinStaObj->ptiClipLock = NULL;
545     bRet = TRUE;
546 
547     if (pWinStaObj->fClipboardChanged)
548     {
549         /* Add synthesized formats - they are rendered later */
550         IntAddSynthesizedFormats(pWinStaObj);
551 
552         /* Notify viewer windows in chain */
553         pWinStaObj->fClipboardChanged = FALSE;
554         if (pWinStaObj->spwndClipViewer)
555         {
556             TRACE("Clipboard: sending WM_DRAWCLIPBOARD to %p\n", pWinStaObj->spwndClipViewer->head.h);
557             // For 32-bit applications this message is sent as a notification
558             co_IntSendMessageNoWait(pWinStaObj->spwndClipViewer->head.h, WM_DRAWCLIPBOARD, 0, 0);
559         }
560     }
561 
562 cleanup:
563     if (pWinStaObj)
564         ObDereferenceObject(pWinStaObj);
565 
566     return bRet;
567 }
568 
569 BOOL APIENTRY
570 NtUserCloseClipboard(VOID)
571 {
572     BOOL bRet;
573 
574     UserEnterExclusive();
575     bRet = UserCloseClipboard();
576     UserLeave();
577 
578     return bRet;
579 }
580 
581 HWND APIENTRY
582 NtUserGetOpenClipboardWindow(VOID)
583 {
584     HWND hWnd = NULL;
585     PWINSTATION_OBJECT pWinStaObj;
586 
587     UserEnterShared();
588 
589     pWinStaObj = IntGetWinStaForCbAccess();
590     if (!pWinStaObj)
591         goto cleanup;
592 
593     if (pWinStaObj->spwndClipOpen)
594         hWnd = pWinStaObj->spwndClipOpen->head.h;
595 
596     ObDereferenceObject(pWinStaObj);
597 
598 cleanup:
599     UserLeave();
600 
601     return hWnd;
602 }
603 
604 BOOL APIENTRY
605 NtUserChangeClipboardChain(HWND hWndRemove, HWND hWndNewNext)
606 {
607     BOOL bRet = FALSE;
608     PWND pWindowRemove;
609     PWINSTATION_OBJECT pWinStaObj;
610 
611     TRACE("NtUserChangeClipboardChain(%p, %p)\n", hWndRemove, hWndNewNext);
612 
613     UserEnterExclusive();
614 
615     pWinStaObj = IntGetWinStaForCbAccess();
616     if (!pWinStaObj)
617         goto cleanup;
618 
619     pWindowRemove = UserGetWindowObject(hWndRemove);
620 
621     if (pWindowRemove && pWinStaObj->spwndClipViewer)
622     {
623         if (pWindowRemove == pWinStaObj->spwndClipViewer)
624             pWinStaObj->spwndClipViewer = UserGetWindowObject(hWndNewNext);
625 
626         if (pWinStaObj->spwndClipViewer)
627             bRet = (BOOL)co_IntSendMessage(pWinStaObj->spwndClipViewer->head.h, WM_CHANGECBCHAIN, (WPARAM)hWndRemove, (LPARAM)hWndNewNext);
628     }
629 
630     ObDereferenceObject(pWinStaObj);
631 
632 cleanup:
633     UserLeave();
634 
635     return bRet;
636 }
637 
638 DWORD APIENTRY
639 NtUserCountClipboardFormats(VOID)
640 {
641     DWORD cFormats = 0;
642     PWINSTATION_OBJECT pWinStaObj;
643 
644     UserEnterShared();
645 
646     pWinStaObj = IntGetWinStaForCbAccess();
647     if (!pWinStaObj)
648         goto cleanup;
649 
650     cFormats = pWinStaObj->cNumClipFormats;
651 
652     ObDereferenceObject(pWinStaObj);
653 
654 cleanup:
655     UserLeave();
656 
657     return cFormats;
658 }
659 
660 BOOL NTAPI
661 UserEmptyClipboard(VOID)
662 {
663     BOOL bRet = FALSE;
664     PWINSTATION_OBJECT pWinStaObj;
665 
666     pWinStaObj = IntGetWinStaForCbAccess();
667     if (!pWinStaObj)
668         return FALSE;
669 
670     /* Check if the clipboard has been opened */
671     if (!IntIsClipboardOpenByMe(pWinStaObj))
672     {
673         EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN);
674         goto cleanup;
675     }
676 
677     UserEmptyClipboardData(pWinStaObj);
678 
679     if (pWinStaObj->spwndClipOwner)
680     {
681         TRACE("Clipboard: WM_DESTROYCLIPBOARD to %p\n", pWinStaObj->spwndClipOwner->head.h);
682         // For 32-bit applications this message is sent as a notification
683         co_IntSendMessage(pWinStaObj->spwndClipOwner->head.h, WM_DESTROYCLIPBOARD, 0, 0);
684     }
685 
686     pWinStaObj->spwndClipOwner = pWinStaObj->spwndClipOpen;
687 
688     pWinStaObj->iClipSerialNumber++;
689     pWinStaObj->iClipSequenceNumber++;
690     pWinStaObj->fClipboardChanged = TRUE;
691     pWinStaObj->fInDelayedRendering = FALSE;
692 
693     bRet = TRUE;
694 
695 cleanup:
696     if (pWinStaObj)
697         ObDereferenceObject(pWinStaObj);
698 
699     return bRet;
700 }
701 
702 BOOL APIENTRY
703 NtUserEmptyClipboard(VOID)
704 {
705     BOOL bRet;
706 
707     TRACE("NtUserEmptyClipboard()\n");
708 
709     UserEnterExclusive();
710     bRet = UserEmptyClipboard();
711     UserLeave();
712 
713     return bRet;
714 }
715 
716 INT APIENTRY
717 NtUserGetClipboardFormatName(UINT fmt, LPWSTR lpszFormatName, INT cchMaxCount)
718 {
719     INT iRet = 0;
720 
721     UserEnterShared();
722 
723     /* If the format is built-in we fail */
724     if (fmt < 0xc000 || fmt > 0xffff)
725     {
726         /* Registetrated formats are >= 0xc000 */
727         goto cleanup;
728     }
729 
730     if (cchMaxCount < 1 || !lpszFormatName)
731     {
732         EngSetLastError(ERROR_INVALID_PARAMETER);
733         goto cleanup;
734     }
735 
736     _SEH2_TRY
737     {
738         ProbeForWrite(lpszFormatName, cchMaxCount * sizeof(WCHAR), 1);
739 
740         iRet = IntGetAtomName((RTL_ATOM)fmt,
741                               lpszFormatName,
742                               cchMaxCount * sizeof(WCHAR));
743         iRet /= sizeof(WCHAR);
744     }
745     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
746     {
747         SetLastNtError(_SEH2_GetExceptionCode());
748     }
749     _SEH2_END;
750 
751 cleanup:
752     UserLeave();
753 
754     return iRet;
755 }
756 
757 HWND APIENTRY
758 NtUserGetClipboardOwner(VOID)
759 {
760     HWND hWnd = NULL;
761     PWINSTATION_OBJECT pWinStaObj;
762 
763     UserEnterShared();
764 
765     pWinStaObj = IntGetWinStaForCbAccess();
766     if (!pWinStaObj)
767         goto cleanup;
768 
769     if (pWinStaObj->spwndClipOwner)
770         hWnd = pWinStaObj->spwndClipOwner->head.h;
771 
772     ObDereferenceObject(pWinStaObj);
773 
774 cleanup:
775     UserLeave();
776 
777     return hWnd;
778 }
779 
780 HWND APIENTRY
781 NtUserGetClipboardViewer(VOID)
782 {
783     HWND hWnd = NULL;
784     PWINSTATION_OBJECT pWinStaObj;
785 
786     UserEnterShared();
787 
788     pWinStaObj = IntGetWinStaForCbAccess();
789     if (!pWinStaObj)
790         goto cleanup;
791 
792     if (pWinStaObj->spwndClipViewer)
793         hWnd = pWinStaObj->spwndClipViewer->head.h;
794 
795     ObDereferenceObject(pWinStaObj);
796 
797 cleanup:
798     UserLeave();
799 
800     return hWnd;
801 }
802 
803 INT APIENTRY
804 NtUserGetPriorityClipboardFormat(UINT *paFormatPriorityList, INT cFormats)
805 {
806     INT i, iRet = 0;
807     PWINSTATION_OBJECT pWinStaObj;
808 
809     UserEnterShared();
810 
811     pWinStaObj = IntGetWinStaForCbAccess();
812     if (!pWinStaObj)
813         goto cleanup;
814 
815     if (pWinStaObj->pClipBase == NULL)
816     {
817         iRet = 0;
818     }
819     else
820     {
821         _SEH2_TRY
822         {
823             ProbeForRead(paFormatPriorityList, cFormats * sizeof(UINT), sizeof(UINT));
824 
825             iRet = -1;
826 
827             for (i = 0; i < cFormats; ++i)
828             {
829                 if (IntIsFormatAvailable(pWinStaObj, paFormatPriorityList[i]))
830                 {
831                     iRet = paFormatPriorityList[i];
832                     break;
833                 }
834             }
835         }
836         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
837         {
838             SetLastNtError(_SEH2_GetExceptionCode());
839         }
840         _SEH2_END;
841     }
842 
843     ObDereferenceObject(pWinStaObj);
844 
845 cleanup:
846     UserLeave();
847 
848     return iRet;
849 
850 }
851 
852 BOOL APIENTRY
853 NtUserIsClipboardFormatAvailable(UINT fmt)
854 {
855     BOOL bRet = FALSE;
856     PWINSTATION_OBJECT pWinStaObj;
857 
858     TRACE("NtUserIsClipboardFormatAvailable(%x)\n", fmt);
859 
860     UserEnterShared();
861 
862     pWinStaObj = IntGetWinStaForCbAccess();
863     if (!pWinStaObj)
864         goto cleanup;
865 
866     if (IntIsFormatAvailable(pWinStaObj, fmt))
867         bRet = TRUE;
868 
869     ObDereferenceObject(pWinStaObj);
870 
871 cleanup:
872     UserLeave();
873 
874     return bRet;
875 }
876 
877 HANDLE APIENTRY
878 NtUserGetClipboardData(UINT fmt, PGETCLIPBDATA pgcd)
879 {
880     HANDLE hRet = NULL;
881     PCLIP pElement;
882     PWINSTATION_OBJECT pWinStaObj;
883 
884     TRACE("NtUserGetClipboardData(%x, %p)\n", fmt, pgcd);
885 
886     UserEnterShared();
887 
888     pWinStaObj = IntGetWinStaForCbAccess();
889     if (!pWinStaObj)
890         goto cleanup;
891 
892     /* Check if the clipboard has been opened */
893     if (!IntIsClipboardOpenByMe(pWinStaObj))
894     {
895         EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN);
896         goto cleanup;
897     }
898 
899     pElement = IntGetFormatElement(pWinStaObj, fmt);
900     if (pElement && IS_DATA_DELAYED(pElement) && pWinStaObj->spwndClipOwner)
901     {
902         /* Send WM_RENDERFORMAT message */
903         pWinStaObj->fInDelayedRendering = TRUE;
904         co_IntSendMessage(pWinStaObj->spwndClipOwner->head.h, WM_RENDERFORMAT, (WPARAM)fmt, 0);
905         pWinStaObj->fInDelayedRendering = FALSE;
906 
907         /* Data should be in clipboard now */
908         pElement = IntGetFormatElement(pWinStaObj, fmt);
909     }
910 
911     if (!pElement || IS_DATA_DELAYED(pElement))
912         goto cleanup;
913 
914     if (IS_DATA_SYNTHESIZED(pElement))
915     {
916         /* Note: Data is synthesized in usermode */
917         /* TODO: Add more formats */
918         switch (fmt)
919         {
920             case CF_UNICODETEXT:
921             case CF_TEXT:
922             case CF_OEMTEXT:
923                 pElement = IntGetFormatElement(pWinStaObj, CF_UNICODETEXT);
924                 if (IS_DATA_SYNTHESIZED(pElement))
925                     pElement = IntGetFormatElement(pWinStaObj, CF_TEXT);
926                 if (IS_DATA_SYNTHESIZED(pElement))
927                     pElement = IntGetFormatElement(pWinStaObj, CF_OEMTEXT);
928                 break;
929             case CF_BITMAP:
930                 IntSynthesizeBitmap(pWinStaObj, pElement);
931                 break;
932             default:
933                 ASSERT(FALSE);
934         }
935     }
936 
937     _SEH2_TRY
938     {
939         ProbeForWrite(pgcd, sizeof(*pgcd), 1);
940         pgcd->uFmtRet = pElement->fmt;
941         pgcd->fGlobalHandle = pElement->fGlobalHandle;
942 
943         /* Text and bitmap needs more data */
944         if (fmt == CF_TEXT)
945         {
946             PCLIP pLocaleEl;
947 
948             pLocaleEl = IntGetFormatElement(pWinStaObj, CF_LOCALE);
949             if (pLocaleEl && !IS_DATA_DELAYED(pLocaleEl))
950                 pgcd->hLocale = pLocaleEl->hData;
951         }
952         else if (fmt == CF_BITMAP)
953         {
954             PCLIP pPaletteEl;
955 
956             pPaletteEl = IntGetFormatElement(pWinStaObj, CF_PALETTE);
957             if (pPaletteEl && !IS_DATA_DELAYED(pPaletteEl))
958                 pgcd->hPalette = pPaletteEl->hData;
959         }
960 
961         hRet = pElement->hData;
962     }
963     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
964     {
965         SetLastNtError(_SEH2_GetExceptionCode());
966     }
967     _SEH2_END;
968 
969 cleanup:
970     if (pWinStaObj)
971         ObDereferenceObject(pWinStaObj);
972 
973     UserLeave();
974 
975     TRACE("NtUserGetClipboardData returns %p\n", hRet);
976 
977     return hRet;
978 }
979 
980 HANDLE NTAPI
981 UserSetClipboardData(UINT fmt, HANDLE hData, PSETCLIPBDATA scd)
982 {
983     HANDLE hRet = NULL;
984     PWINSTATION_OBJECT pWinStaObj;
985 
986     pWinStaObj = IntGetWinStaForCbAccess();
987     if (!pWinStaObj)
988         goto cleanup;
989 
990     if (!fmt || !pWinStaObj->ptiClipLock)
991     {
992         ERR("Access denied!\n");
993         EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN);
994         goto cleanup;
995     }
996 
997     if (scd->fIncSerialNumber)
998         pWinStaObj->iClipSerialNumber++;
999 
1000     /* Is it a delayed rendering? */
1001     if (hData)
1002     {
1003         /* Is it a bitmap? */
1004         if (fmt == CF_BITMAP)
1005         {
1006             /* Make bitmap public */
1007             GreSetObjectOwner(hData, GDI_OBJ_HMGR_PUBLIC);
1008         }
1009 
1010         /* Save data in the clipboard */
1011         IntAddFormatedData(pWinStaObj, fmt, hData, scd->fGlobalHandle, FALSE);
1012         TRACE("hData stored\n");
1013 
1014         /* If the serial number was increased, increase also the sequence number */
1015         if (scd->fIncSerialNumber)
1016             pWinStaObj->iClipSequenceNumber++;
1017 
1018         pWinStaObj->fClipboardChanged = TRUE;
1019 
1020         /* Note: Synthesized formats are added in NtUserCloseClipboard */
1021     }
1022     else
1023     {
1024         /* This is a delayed rendering */
1025         IntAddFormatedData(pWinStaObj, fmt, DATA_DELAYED, FALSE, FALSE);
1026         TRACE("SetClipboardData delayed format: %u\n", fmt);
1027     }
1028 
1029     /* Return hData on success */
1030     hRet = hData;
1031 
1032 cleanup:
1033     TRACE("NtUserSetClipboardData returns: %p\n", hRet);
1034 
1035     if (pWinStaObj)
1036         ObDereferenceObject(pWinStaObj);
1037 
1038     return hRet;
1039 }
1040 
1041 HANDLE APIENTRY
1042 NtUserSetClipboardData(UINT fmt, HANDLE hData, PSETCLIPBDATA pUnsafeScd)
1043 {
1044     SETCLIPBDATA scd;
1045     HANDLE hRet;
1046 
1047     TRACE("NtUserSetClipboardData(%x %p %p)\n", fmt, hData, pUnsafeScd);
1048 
1049     _SEH2_TRY
1050     {
1051         ProbeForRead(pUnsafeScd, sizeof(*pUnsafeScd), 1);
1052         RtlCopyMemory(&scd, pUnsafeScd, sizeof(scd));
1053     }
1054     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1055     {
1056         SetLastNtError(_SEH2_GetExceptionCode());
1057         _SEH2_YIELD(return NULL;)
1058     }
1059     _SEH2_END
1060 
1061     UserEnterExclusive();
1062 
1063     /* Call internal function */
1064     hRet = UserSetClipboardData(fmt, hData, &scd);
1065 
1066     UserLeave();
1067 
1068     return hRet;
1069 }
1070 
1071 HWND APIENTRY
1072 NtUserSetClipboardViewer(HWND hWndNewViewer)
1073 {
1074     HWND hWndNext = NULL;
1075     PWINSTATION_OBJECT pWinStaObj;
1076     PWND pWindow;
1077 
1078     UserEnterExclusive();
1079 
1080     pWinStaObj = IntGetWinStaForCbAccess();
1081     if (!pWinStaObj)
1082         goto cleanup;
1083 
1084     pWindow = UserGetWindowObject(hWndNewViewer);
1085     if (!pWindow)
1086     {
1087         EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
1088         goto cleanup;
1089     }
1090 
1091     /* Return previous viewer. New viever window should
1092        send messages to rest of the chain */
1093     if (pWinStaObj->spwndClipViewer)
1094         hWndNext = pWinStaObj->spwndClipViewer->head.h;
1095 
1096     /* Set new viewer window */
1097     pWinStaObj->spwndClipViewer = pWindow;
1098 
1099     /* Notify viewer windows in chain */
1100     pWinStaObj->fClipboardChanged = FALSE;
1101     if (pWinStaObj->spwndClipViewer)
1102     {
1103         TRACE("Clipboard: sending WM_DRAWCLIPBOARD to %p\n", pWinStaObj->spwndClipViewer->head.h);
1104         // For 32-bit applications this message is sent as a notification
1105         co_IntSendMessageNoWait(pWinStaObj->spwndClipViewer->head.h, WM_DRAWCLIPBOARD, 0, 0);
1106     }
1107 
1108 cleanup:
1109     if (pWinStaObj)
1110         ObDereferenceObject(pWinStaObj);
1111 
1112     UserLeave();
1113 
1114     return hWndNext;
1115 }
1116 
1117 // Sequence number is incremented whenever the contents of the clipboard change
1118 // or the clipboard is emptied. If clipboard rendering is delayed,
1119 // the sequence number is not incremented until the changes are rendered.
1120 
1121 DWORD APIENTRY
1122 NtUserGetClipboardSequenceNumber(VOID)
1123 {
1124     DWORD dwRet = 0;
1125     PWINSTATION_OBJECT pWinStaObj;
1126 
1127     UserEnterShared();
1128 
1129     pWinStaObj = IntGetWinStaForCbAccess();
1130     if (!pWinStaObj)
1131         goto cleanup;
1132 
1133     /* Get windowstation sequence number */
1134     dwRet = (DWORD)pWinStaObj->iClipSequenceNumber;
1135 
1136     ObDereferenceObject(pWinStaObj);
1137 
1138 cleanup:
1139     UserLeave();
1140 
1141     return dwRet;
1142 }
1143 
1144 HANDLE APIENTRY
1145 NtUserConvertMemHandle(
1146    PVOID pData,
1147    DWORD cbData)
1148 {
1149     HANDLE hMem = NULL;
1150     PCLIPBOARDDATA pMemObj;
1151 
1152     UserEnterExclusive();
1153 
1154     /* Create Clipboard data object */
1155     pMemObj = UserCreateObject(gHandleTable, NULL, NULL, &hMem, TYPE_CLIPDATA, sizeof(CLIPBOARDDATA) + cbData);
1156     if (!pMemObj)
1157         goto cleanup;
1158 
1159     pMemObj->cbData = cbData;
1160 
1161     /* Copy data */
1162     _SEH2_TRY
1163     {
1164         ProbeForRead(pData, cbData, 1);
1165         memcpy(pMemObj->Data, pData, cbData);
1166     }
1167     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1168     {
1169         pMemObj = NULL;
1170     }
1171     _SEH2_END;
1172 
1173     /* Release the extra reference (UserCreateObject added 2 references) */
1174     UserDereferenceObject(pMemObj);
1175 
1176     /* If we failed to copy data, remove handle */
1177     if (!pMemObj)
1178     {
1179         UserDeleteObject(hMem, TYPE_CLIPDATA);
1180         hMem = NULL;
1181     }
1182 
1183 cleanup:
1184     UserLeave();
1185 
1186     return hMem;
1187 }
1188 
1189 NTSTATUS APIENTRY
1190 NtUserCreateLocalMemHandle(
1191    HANDLE hMem,
1192    PVOID pData,
1193    DWORD cbData,
1194    DWORD *pcbData)
1195 {
1196     PCLIPBOARDDATA pMemObj;
1197     NTSTATUS Status = STATUS_SUCCESS;
1198 
1199     UserEnterShared();
1200 
1201     /* Get Clipboard data object */
1202     pMemObj = (PCLIPBOARDDATA)UserGetObject(gHandleTable, hMem, TYPE_CLIPDATA);
1203     if (!pMemObj)
1204     {
1205         Status = STATUS_INVALID_HANDLE;
1206         goto cleanup;
1207     }
1208 
1209     /* Don't overrun */
1210     if (cbData > pMemObj->cbData)
1211         cbData = pMemObj->cbData;
1212 
1213     /* Copy data to usermode */
1214     _SEH2_TRY
1215     {
1216         if (pcbData)
1217         {
1218             ProbeForWrite(pcbData, sizeof(*pcbData), 1);
1219             *pcbData = pMemObj->cbData;
1220         }
1221 
1222         ProbeForWrite(pData, cbData, 1);
1223         memcpy(pData, pMemObj->Data, cbData);
1224     }
1225     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1226     {
1227         Status = _SEH2_GetExceptionCode();
1228     }
1229     _SEH2_END;
1230 
1231 cleanup:
1232     UserLeave();
1233 
1234     return Status;
1235 }
1236 
1237 /* EOF */
1238