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