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