xref: /reactos/win32ss/user/ntuser/clipboard.c (revision ebaf247c)
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     pWinStaObj = IntGetWinStaForCbAccess();
379     if (!pWinStaObj)
380         return;
381 
382     co_IntSendMessage(pWinStaObj->spwndClipOwner->head.h, WM_RENDERALLFORMATS, 0, 0);
383 
384     /* If the window being destroyed is the current clipboard owner... */
385     if (pWindow == pWinStaObj->spwndClipOwner)
386     {
387         /* ... make it release the clipboard */
388         pWinStaObj->spwndClipOwner = NULL;
389     }
390 
391     if (pWinStaObj->fClipboardChanged)
392     {
393         /* Add synthesized formats - they are rendered later */
394         IntAddSynthesizedFormats(pWinStaObj);
395 
396         /* Notify viewer windows in chain */
397         pWinStaObj->fClipboardChanged = FALSE;
398         if (pWinStaObj->spwndClipViewer)
399         {
400             TRACE("Clipboard: sending WM_DRAWCLIPBOARD to %p\n", pWinStaObj->spwndClipViewer->head.h);
401             // For 32-bit applications this message is sent as a notification
402             co_IntSendMessageNoWait(pWinStaObj->spwndClipViewer->head.h, WM_DRAWCLIPBOARD, 0, 0);
403         }
404     }
405 
406     ObDereferenceObject(pWinStaObj);
407 }
408 
409 /* UserClipboardFreeWindow is called from co_UserFreeWindow in window.c */
410 VOID FASTCALL
411 UserClipboardFreeWindow(PWND pWindow)
412 {
413     PWINSTATION_OBJECT pWinStaObj;
414 
415     pWinStaObj = IntGetWinStaForCbAccess();
416     if (!pWinStaObj)
417         return;
418 
419     if (pWindow == pWinStaObj->spwndClipOwner)
420     {
421         /* The owner window was destroyed */
422         pWinStaObj->spwndClipOwner = NULL;
423     }
424 
425     /* Check if clipboard is not locked by this window, if yes, unlock it */
426     if (pWindow == pWinStaObj->spwndClipOpen)
427     {
428         /* The window that opens the clipboard was destroyed */
429         pWinStaObj->spwndClipOpen = NULL;
430         pWinStaObj->ptiClipLock = NULL;
431     }
432     /* Remove window from window chain */
433     if (pWindow == pWinStaObj->spwndClipViewer)
434         pWinStaObj->spwndClipViewer = NULL;
435 
436     ObDereferenceObject(pWinStaObj);
437 }
438 
439 UINT APIENTRY
440 UserEnumClipboardFormats(UINT fmt)
441 {
442     UINT Ret = 0;
443     PCLIP pElement;
444     PWINSTATION_OBJECT pWinStaObj;
445 
446     pWinStaObj = IntGetWinStaForCbAccess();
447     if (!pWinStaObj)
448         goto cleanup;
449 
450     /* Check if the clipboard has been opened */
451     if (!IntIsClipboardOpenByMe(pWinStaObj))
452     {
453         EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN);
454         goto cleanup;
455     }
456 
457     if (fmt == 0)
458     {
459         /* Return first format */
460         if (pWinStaObj->pClipBase)
461             Ret = pWinStaObj->pClipBase[0].fmt;
462     }
463     else
464     {
465         /* Return next format */
466         pElement = IntGetFormatElement(pWinStaObj, fmt);
467         if (pElement != NULL)
468         {
469             ++pElement;
470             if (pElement < &pWinStaObj->pClipBase[pWinStaObj->cNumClipFormats])
471             {
472                 Ret = pElement->fmt;
473             }
474         }
475     }
476 
477 cleanup:
478     if (pWinStaObj)
479         ObDereferenceObject(pWinStaObj);
480 
481     return Ret;
482 }
483 
484 BOOL NTAPI
485 UserOpenClipboard(HWND hWnd)
486 {
487     PWND pWindow = NULL;
488     BOOL bRet = FALSE;
489     PWINSTATION_OBJECT pWinStaObj = NULL;
490 
491     if (hWnd)
492     {
493         pWindow = UserGetWindowObject(hWnd);
494         if (!pWindow)
495             goto cleanup;
496     }
497 
498     pWinStaObj = IntGetWinStaForCbAccess();
499     if (!pWinStaObj)
500         goto cleanup;
501 
502     /* Check if we already opened the clipboard */
503     if ((pWindow == pWinStaObj->spwndClipOpen) && IntIsClipboardOpenByMe(pWinStaObj))
504     {
505         bRet = TRUE;
506         goto cleanup;
507     }
508 
509     /* If the clipboard was already opened by somebody else, bail out */
510     if ((pWindow != pWinStaObj->spwndClipOpen) && pWinStaObj->ptiClipLock)
511     {
512         ERR("Access denied!\n");
513         EngSetLastError(ERROR_ACCESS_DENIED);
514         goto cleanup;
515     }
516 
517     /* Open the clipboard */
518     pWinStaObj->spwndClipOpen = pWindow;
519     pWinStaObj->ptiClipLock = PsGetCurrentThreadWin32Thread();
520     bRet = TRUE;
521 
522 cleanup:
523     if (pWinStaObj)
524         ObDereferenceObject(pWinStaObj);
525 
526     return bRet;
527 }
528 
529 BOOL APIENTRY
530 NtUserOpenClipboard(HWND hWnd, DWORD Unknown1)
531 {
532     BOOL bRet;
533 
534     UserEnterExclusive();
535     bRet = UserOpenClipboard(hWnd);
536     UserLeave();
537 
538     return bRet;
539 }
540 
541 BOOL NTAPI
542 UserCloseClipboard(VOID)
543 {
544     BOOL bRet = FALSE;
545     PWINSTATION_OBJECT pWinStaObj;
546 
547     pWinStaObj = IntGetWinStaForCbAccess();
548     if (!pWinStaObj)
549         goto cleanup;
550 
551     /* Check if the clipboard has been opened */
552     if (!IntIsClipboardOpenByMe(pWinStaObj))
553     {
554         EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN);
555         goto cleanup;
556     }
557 
558     /* Clipboard is no longer open */
559     pWinStaObj->spwndClipOpen = NULL;
560     pWinStaObj->ptiClipLock = NULL;
561     bRet = TRUE;
562 
563     if (pWinStaObj->fClipboardChanged)
564     {
565         /* Add synthesized formats - they are rendered later */
566         IntAddSynthesizedFormats(pWinStaObj);
567 
568         /* Notify viewer windows in chain */
569         pWinStaObj->fClipboardChanged = FALSE;
570         if (pWinStaObj->spwndClipViewer)
571         {
572             TRACE("Clipboard: sending WM_DRAWCLIPBOARD to %p\n", pWinStaObj->spwndClipViewer->head.h);
573             // For 32-bit applications this message is sent as a notification
574             co_IntSendMessageNoWait(pWinStaObj->spwndClipViewer->head.h, WM_DRAWCLIPBOARD, 0, 0);
575         }
576     }
577 
578 cleanup:
579     if (pWinStaObj)
580         ObDereferenceObject(pWinStaObj);
581 
582     return bRet;
583 }
584 
585 BOOL APIENTRY
586 NtUserCloseClipboard(VOID)
587 {
588     BOOL bRet;
589 
590     UserEnterExclusive();
591     bRet = UserCloseClipboard();
592     UserLeave();
593 
594     return bRet;
595 }
596 
597 HWND APIENTRY
598 NtUserGetOpenClipboardWindow(VOID)
599 {
600     HWND hWnd = NULL;
601     PWINSTATION_OBJECT pWinStaObj;
602 
603     UserEnterShared();
604 
605     pWinStaObj = IntGetWinStaForCbAccess();
606     if (!pWinStaObj)
607         goto cleanup;
608 
609     if (pWinStaObj->spwndClipOpen)
610         hWnd = pWinStaObj->spwndClipOpen->head.h;
611 
612     ObDereferenceObject(pWinStaObj);
613 
614 cleanup:
615     UserLeave();
616 
617     return hWnd;
618 }
619 
620 BOOL APIENTRY
621 NtUserChangeClipboardChain(HWND hWndRemove, HWND hWndNewNext)
622 {
623     BOOL bRet = FALSE;
624     PWND pWindowRemove;
625     PWINSTATION_OBJECT pWinStaObj;
626 
627     TRACE("NtUserChangeClipboardChain(%p, %p)\n", hWndRemove, hWndNewNext);
628 
629     UserEnterExclusive();
630 
631     pWinStaObj = IntGetWinStaForCbAccess();
632     if (!pWinStaObj)
633         goto cleanup;
634 
635     pWindowRemove = UserGetWindowObject(hWndRemove);
636 
637     if (pWindowRemove && pWinStaObj->spwndClipViewer)
638     {
639         if (pWindowRemove == pWinStaObj->spwndClipViewer)
640             pWinStaObj->spwndClipViewer = UserGetWindowObject(hWndNewNext);
641 
642         if (pWinStaObj->spwndClipViewer)
643             bRet = (BOOL)co_IntSendMessage(pWinStaObj->spwndClipViewer->head.h, WM_CHANGECBCHAIN, (WPARAM)hWndRemove, (LPARAM)hWndNewNext);
644     }
645 
646     ObDereferenceObject(pWinStaObj);
647 
648 cleanup:
649     UserLeave();
650 
651     return bRet;
652 }
653 
654 DWORD APIENTRY
655 NtUserCountClipboardFormats(VOID)
656 {
657     DWORD cFormats = 0;
658     PWINSTATION_OBJECT pWinStaObj;
659 
660     UserEnterShared();
661 
662     pWinStaObj = IntGetWinStaForCbAccess();
663     if (!pWinStaObj)
664         goto cleanup;
665 
666     cFormats = pWinStaObj->cNumClipFormats;
667 
668     ObDereferenceObject(pWinStaObj);
669 
670 cleanup:
671     UserLeave();
672 
673     return cFormats;
674 }
675 
676 BOOL NTAPI
677 UserEmptyClipboard(VOID)
678 {
679     BOOL bRet = FALSE;
680     PWINSTATION_OBJECT pWinStaObj;
681 
682     pWinStaObj = IntGetWinStaForCbAccess();
683     if (!pWinStaObj)
684         return FALSE;
685 
686     /* Check if the clipboard has been opened */
687     if (!IntIsClipboardOpenByMe(pWinStaObj))
688     {
689         EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN);
690         goto cleanup;
691     }
692 
693     UserEmptyClipboardData(pWinStaObj);
694 
695     if (pWinStaObj->spwndClipOwner)
696     {
697         TRACE("Clipboard: WM_DESTROYCLIPBOARD to %p\n", pWinStaObj->spwndClipOwner->head.h);
698         // For 32-bit applications this message is sent as a notification
699         co_IntSendMessage(pWinStaObj->spwndClipOwner->head.h, WM_DESTROYCLIPBOARD, 0, 0);
700     }
701 
702     pWinStaObj->spwndClipOwner = pWinStaObj->spwndClipOpen;
703 
704     pWinStaObj->iClipSerialNumber++;
705     pWinStaObj->iClipSequenceNumber++;
706     pWinStaObj->fClipboardChanged = TRUE;
707     pWinStaObj->fInDelayedRendering = FALSE;
708 
709     bRet = TRUE;
710 
711 cleanup:
712     if (pWinStaObj)
713         ObDereferenceObject(pWinStaObj);
714 
715     return bRet;
716 }
717 
718 BOOL APIENTRY
719 NtUserEmptyClipboard(VOID)
720 {
721     BOOL bRet;
722 
723     TRACE("NtUserEmptyClipboard()\n");
724 
725     UserEnterExclusive();
726     bRet = UserEmptyClipboard();
727     UserLeave();
728 
729     return bRet;
730 }
731 
732 INT APIENTRY
733 NtUserGetClipboardFormatName(UINT fmt, LPWSTR lpszFormatName, INT cchMaxCount)
734 {
735     INT iRet = 0;
736 
737     UserEnterShared();
738 
739     /* If the format is built-in we fail */
740     if (fmt < 0xc000 || fmt > 0xffff)
741     {
742         /* Registetrated formats are >= 0xc000 */
743         goto cleanup;
744     }
745 
746     if (cchMaxCount < 1 || !lpszFormatName)
747     {
748         EngSetLastError(ERROR_INVALID_PARAMETER);
749         goto cleanup;
750     }
751 
752     _SEH2_TRY
753     {
754         ProbeForWrite(lpszFormatName, cchMaxCount * sizeof(WCHAR), 1);
755 
756         iRet = IntGetAtomName((RTL_ATOM)fmt,
757                               lpszFormatName,
758                               cchMaxCount * sizeof(WCHAR));
759         iRet /= sizeof(WCHAR);
760     }
761     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
762     {
763         SetLastNtError(_SEH2_GetExceptionCode());
764     }
765     _SEH2_END;
766 
767 cleanup:
768     UserLeave();
769 
770     return iRet;
771 }
772 
773 HWND APIENTRY
774 NtUserGetClipboardOwner(VOID)
775 {
776     HWND hWnd = NULL;
777     PWINSTATION_OBJECT pWinStaObj;
778 
779     UserEnterShared();
780 
781     pWinStaObj = IntGetWinStaForCbAccess();
782     if (!pWinStaObj)
783         goto cleanup;
784 
785     if (pWinStaObj->spwndClipOwner)
786         hWnd = pWinStaObj->spwndClipOwner->head.h;
787 
788     ObDereferenceObject(pWinStaObj);
789 
790 cleanup:
791     UserLeave();
792 
793     return hWnd;
794 }
795 
796 HWND APIENTRY
797 NtUserGetClipboardViewer(VOID)
798 {
799     HWND hWnd = NULL;
800     PWINSTATION_OBJECT pWinStaObj;
801 
802     UserEnterShared();
803 
804     pWinStaObj = IntGetWinStaForCbAccess();
805     if (!pWinStaObj)
806         goto cleanup;
807 
808     if (pWinStaObj->spwndClipViewer)
809         hWnd = pWinStaObj->spwndClipViewer->head.h;
810 
811     ObDereferenceObject(pWinStaObj);
812 
813 cleanup:
814     UserLeave();
815 
816     return hWnd;
817 }
818 
819 INT APIENTRY
820 NtUserGetPriorityClipboardFormat(UINT *paFormatPriorityList, INT cFormats)
821 {
822     INT i, iRet = 0;
823     PWINSTATION_OBJECT pWinStaObj;
824 
825     UserEnterShared();
826 
827     pWinStaObj = IntGetWinStaForCbAccess();
828     if (!pWinStaObj)
829         goto cleanup;
830 
831     if (pWinStaObj->pClipBase == NULL)
832     {
833         iRet = 0;
834     }
835     else
836     {
837         _SEH2_TRY
838         {
839             ProbeForRead(paFormatPriorityList, cFormats * sizeof(UINT), sizeof(UINT));
840 
841             iRet = -1;
842 
843             for (i = 0; i < cFormats; ++i)
844             {
845                 if (IntIsFormatAvailable(pWinStaObj, paFormatPriorityList[i]))
846                 {
847                     iRet = paFormatPriorityList[i];
848                     break;
849                 }
850             }
851         }
852         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
853         {
854             SetLastNtError(_SEH2_GetExceptionCode());
855         }
856         _SEH2_END;
857     }
858 
859     ObDereferenceObject(pWinStaObj);
860 
861 cleanup:
862     UserLeave();
863 
864     return iRet;
865 
866 }
867 
868 BOOL APIENTRY
869 NtUserIsClipboardFormatAvailable(UINT fmt)
870 {
871     BOOL bRet = FALSE;
872     PWINSTATION_OBJECT pWinStaObj;
873 
874     TRACE("NtUserIsClipboardFormatAvailable(%x)\n", fmt);
875 
876     UserEnterShared();
877 
878     pWinStaObj = IntGetWinStaForCbAccess();
879     if (!pWinStaObj)
880         goto cleanup;
881 
882     if (IntIsFormatAvailable(pWinStaObj, fmt))
883         bRet = TRUE;
884 
885     ObDereferenceObject(pWinStaObj);
886 
887 cleanup:
888     UserLeave();
889 
890     return bRet;
891 }
892 
893 HANDLE APIENTRY
894 NtUserGetClipboardData(UINT fmt, PGETCLIPBDATA pgcd)
895 {
896     HANDLE hRet = NULL;
897     PCLIP pElement;
898     PWINSTATION_OBJECT pWinStaObj;
899     UINT uSourceFmt = fmt;
900 
901     TRACE("NtUserGetClipboardData(%x, %p)\n", fmt, pgcd);
902 
903     UserEnterShared();
904 
905     pWinStaObj = IntGetWinStaForCbAccess();
906     if (!pWinStaObj)
907         goto cleanup;
908 
909     /* Check if the clipboard has been opened */
910     if (!IntIsClipboardOpenByMe(pWinStaObj))
911     {
912         EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN);
913         goto cleanup;
914     }
915 
916     pElement = IntGetFormatElement(pWinStaObj, fmt);
917     if (!pElement)
918         goto cleanup;
919 
920     if (IS_DATA_SYNTHESIZED(pElement))
921     {
922         /* Note: Data is synthesized in usermode */
923         /* TODO: Add more formats */
924         switch (fmt)
925         {
926             case CF_UNICODETEXT:
927             case CF_TEXT:
928             case CF_OEMTEXT:
929                 uSourceFmt = CF_UNICODETEXT;
930                 pElement = IntGetFormatElement(pWinStaObj, uSourceFmt);
931                 if (IS_DATA_SYNTHESIZED(pElement))
932                 {
933                     uSourceFmt = CF_TEXT;
934                     pElement = IntGetFormatElement(pWinStaObj, uSourceFmt);
935                 }
936                 if (IS_DATA_SYNTHESIZED(pElement))
937                 {
938                     uSourceFmt = CF_OEMTEXT;
939                     pElement = IntGetFormatElement(pWinStaObj, uSourceFmt);
940                 }
941                 break;
942 
943             case CF_BITMAP:
944                 IntSynthesizeBitmap(pWinStaObj, pElement);
945                 break;
946 
947             case CF_METAFILEPICT:
948                 uSourceFmt = CF_ENHMETAFILE;
949                 pElement = IntGetFormatElement(pWinStaObj, uSourceFmt);
950                 break;
951 
952             case CF_ENHMETAFILE:
953                 uSourceFmt = CF_METAFILEPICT;
954                 pElement = IntGetFormatElement(pWinStaObj, uSourceFmt);
955                 break;
956 
957             default:
958                 ASSERT(FALSE);
959         }
960     }
961 
962     if (pElement && IS_DATA_DELAYED(pElement) && pWinStaObj->spwndClipOwner)
963     {
964         /* Send WM_RENDERFORMAT message */
965         pWinStaObj->fInDelayedRendering = TRUE;
966         co_IntSendMessage(pWinStaObj->spwndClipOwner->head.h, WM_RENDERFORMAT, (WPARAM)uSourceFmt, 0);
967         pWinStaObj->fInDelayedRendering = FALSE;
968 
969         /* Data should be in clipboard now */
970         pElement = IntGetFormatElement(pWinStaObj, uSourceFmt);
971     }
972 
973     if (!pElement || IS_DATA_DELAYED(pElement))
974         goto cleanup;
975 
976     _SEH2_TRY
977     {
978         ProbeForWrite(pgcd, sizeof(*pgcd), 1);
979         pgcd->uFmtRet = pElement->fmt;
980         pgcd->fGlobalHandle = pElement->fGlobalHandle;
981 
982         /* Text and bitmap needs more data */
983         if (fmt == CF_TEXT)
984         {
985             PCLIP pLocaleEl;
986 
987             pLocaleEl = IntGetFormatElement(pWinStaObj, CF_LOCALE);
988             if (pLocaleEl && !IS_DATA_DELAYED(pLocaleEl))
989                 pgcd->hLocale = pLocaleEl->hData;
990         }
991         else if (fmt == CF_BITMAP)
992         {
993             PCLIP pPaletteEl;
994 
995             pPaletteEl = IntGetFormatElement(pWinStaObj, CF_PALETTE);
996             if (pPaletteEl && !IS_DATA_DELAYED(pPaletteEl))
997                 pgcd->hPalette = pPaletteEl->hData;
998         }
999 
1000         hRet = pElement->hData;
1001     }
1002     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1003     {
1004         SetLastNtError(_SEH2_GetExceptionCode());
1005     }
1006     _SEH2_END;
1007 
1008 cleanup:
1009     if (pWinStaObj)
1010         ObDereferenceObject(pWinStaObj);
1011 
1012     UserLeave();
1013 
1014     TRACE("NtUserGetClipboardData returns %p\n", hRet);
1015 
1016     return hRet;
1017 }
1018 
1019 HANDLE NTAPI
1020 UserSetClipboardData(UINT fmt, HANDLE hData, PSETCLIPBDATA scd)
1021 {
1022     HANDLE hRet = NULL;
1023     PWINSTATION_OBJECT pWinStaObj;
1024 
1025     pWinStaObj = IntGetWinStaForCbAccess();
1026     if (!pWinStaObj)
1027         goto cleanup;
1028 
1029     if (!fmt || !pWinStaObj->ptiClipLock)
1030     {
1031         ERR("Access denied!\n");
1032         EngSetLastError(ERROR_CLIPBOARD_NOT_OPEN);
1033         goto cleanup;
1034     }
1035 
1036     if (scd->fIncSerialNumber)
1037         pWinStaObj->iClipSerialNumber++;
1038 
1039     /* Is it a delayed rendering? */
1040     if (hData)
1041     {
1042         /* Is it a bitmap? */
1043         if (fmt == CF_BITMAP)
1044         {
1045             /* Make bitmap public */
1046             GreSetObjectOwner(hData, GDI_OBJ_HMGR_PUBLIC);
1047         }
1048 
1049         /* Save data in the clipboard */
1050         IntAddFormatedData(pWinStaObj, fmt, hData, scd->fGlobalHandle, FALSE);
1051         TRACE("hData stored\n");
1052 
1053         /* If the serial number was increased, increase also the sequence number */
1054         if (scd->fIncSerialNumber)
1055             pWinStaObj->iClipSequenceNumber++;
1056 
1057         pWinStaObj->fClipboardChanged = TRUE;
1058 
1059         /* Note: Synthesized formats are added in NtUserCloseClipboard */
1060     }
1061     else
1062     {
1063         /* This is a delayed rendering */
1064         IntAddFormatedData(pWinStaObj, fmt, DATA_DELAYED, FALSE, FALSE);
1065         TRACE("SetClipboardData delayed format: %u\n", fmt);
1066     }
1067 
1068     /* Return hData on success */
1069     hRet = hData;
1070 
1071 cleanup:
1072     TRACE("NtUserSetClipboardData returns: %p\n", hRet);
1073 
1074     if (pWinStaObj)
1075         ObDereferenceObject(pWinStaObj);
1076 
1077     return hRet;
1078 }
1079 
1080 HANDLE APIENTRY
1081 NtUserSetClipboardData(UINT fmt, HANDLE hData, PSETCLIPBDATA pUnsafeScd)
1082 {
1083     SETCLIPBDATA scd;
1084     HANDLE hRet;
1085 
1086     TRACE("NtUserSetClipboardData(%x %p %p)\n", fmt, hData, pUnsafeScd);
1087 
1088     _SEH2_TRY
1089     {
1090         ProbeForRead(pUnsafeScd, sizeof(*pUnsafeScd), 1);
1091         RtlCopyMemory(&scd, pUnsafeScd, sizeof(scd));
1092     }
1093     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1094     {
1095         SetLastNtError(_SEH2_GetExceptionCode());
1096         _SEH2_YIELD(return NULL;)
1097     }
1098     _SEH2_END
1099 
1100     UserEnterExclusive();
1101 
1102     /* Call internal function */
1103     hRet = UserSetClipboardData(fmt, hData, &scd);
1104 
1105     UserLeave();
1106 
1107     return hRet;
1108 }
1109 
1110 HWND APIENTRY
1111 NtUserSetClipboardViewer(HWND hWndNewViewer)
1112 {
1113     HWND hWndNext = NULL;
1114     PWINSTATION_OBJECT pWinStaObj;
1115     PWND pWindow;
1116 
1117     UserEnterExclusive();
1118 
1119     pWinStaObj = IntGetWinStaForCbAccess();
1120     if (!pWinStaObj)
1121         goto cleanup;
1122 
1123     pWindow = UserGetWindowObject(hWndNewViewer);
1124     if (!pWindow)
1125     {
1126         EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
1127         goto cleanup;
1128     }
1129 
1130     /* Return previous viewer. New viever window should
1131        send messages to rest of the chain */
1132     if (pWinStaObj->spwndClipViewer)
1133         hWndNext = pWinStaObj->spwndClipViewer->head.h;
1134 
1135     /* Set new viewer window */
1136     pWinStaObj->spwndClipViewer = pWindow;
1137 
1138     /* Notify viewer windows in chain */
1139     pWinStaObj->fClipboardChanged = FALSE;
1140     if (pWinStaObj->spwndClipViewer)
1141     {
1142         TRACE("Clipboard: sending WM_DRAWCLIPBOARD to %p\n", pWinStaObj->spwndClipViewer->head.h);
1143         // For 32-bit applications this message is sent as a notification
1144         co_IntSendMessageNoWait(pWinStaObj->spwndClipViewer->head.h, WM_DRAWCLIPBOARD, 0, 0);
1145     }
1146 
1147 cleanup:
1148     if (pWinStaObj)
1149         ObDereferenceObject(pWinStaObj);
1150 
1151     UserLeave();
1152 
1153     return hWndNext;
1154 }
1155 
1156 // Sequence number is incremented whenever the contents of the clipboard change
1157 // or the clipboard is emptied. If clipboard rendering is delayed,
1158 // the sequence number is not incremented until the changes are rendered.
1159 
1160 DWORD APIENTRY
1161 NtUserGetClipboardSequenceNumber(VOID)
1162 {
1163     DWORD dwRet = 0;
1164     PWINSTATION_OBJECT pWinStaObj;
1165 
1166     UserEnterShared();
1167 
1168     pWinStaObj = IntGetWinStaForCbAccess();
1169     if (!pWinStaObj)
1170         goto cleanup;
1171 
1172     /* Get windowstation sequence number */
1173     dwRet = (DWORD)pWinStaObj->iClipSequenceNumber;
1174 
1175     ObDereferenceObject(pWinStaObj);
1176 
1177 cleanup:
1178     UserLeave();
1179 
1180     return dwRet;
1181 }
1182 
1183 HANDLE APIENTRY
1184 NtUserConvertMemHandle(
1185    PVOID pData,
1186    DWORD cbData)
1187 {
1188     HANDLE hMem = NULL;
1189     PCLIPBOARDDATA pMemObj;
1190 
1191     UserEnterExclusive();
1192 
1193     /* Create Clipboard data object */
1194     pMemObj = UserCreateObject(gHandleTable, NULL, NULL, &hMem, TYPE_CLIPDATA, sizeof(CLIPBOARDDATA) + cbData);
1195     if (!pMemObj)
1196         goto cleanup;
1197 
1198     pMemObj->cbData = cbData;
1199 
1200     /* Copy data */
1201     _SEH2_TRY
1202     {
1203         ProbeForRead(pData, cbData, 1);
1204         memcpy(pMemObj->Data, pData, cbData);
1205     }
1206     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1207     {
1208         pMemObj = NULL;
1209     }
1210     _SEH2_END;
1211 
1212     /* Release the extra reference (UserCreateObject added 2 references) */
1213     UserDereferenceObject(pMemObj);
1214 
1215     /* If we failed to copy data, remove handle */
1216     if (!pMemObj)
1217     {
1218         UserDeleteObject(hMem, TYPE_CLIPDATA);
1219         hMem = NULL;
1220     }
1221 
1222 cleanup:
1223     UserLeave();
1224 
1225     return hMem;
1226 }
1227 
1228 NTSTATUS APIENTRY
1229 NtUserCreateLocalMemHandle(
1230    HANDLE hMem,
1231    PVOID pData,
1232    DWORD cbData,
1233    DWORD *pcbData)
1234 {
1235     PCLIPBOARDDATA pMemObj;
1236     NTSTATUS Status = STATUS_SUCCESS;
1237 
1238     UserEnterShared();
1239 
1240     /* Get Clipboard data object */
1241     pMemObj = (PCLIPBOARDDATA)UserGetObject(gHandleTable, hMem, TYPE_CLIPDATA);
1242     if (!pMemObj)
1243     {
1244         Status = STATUS_INVALID_HANDLE;
1245         goto cleanup;
1246     }
1247 
1248     /* Don't overrun */
1249     if (cbData > pMemObj->cbData)
1250         cbData = pMemObj->cbData;
1251 
1252     /* Copy data to usermode */
1253     _SEH2_TRY
1254     {
1255         if (pcbData)
1256         {
1257             ProbeForWrite(pcbData, sizeof(*pcbData), 1);
1258             *pcbData = pMemObj->cbData;
1259         }
1260 
1261         ProbeForWrite(pData, cbData, 1);
1262         memcpy(pData, pMemObj->Data, cbData);
1263     }
1264     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1265     {
1266         Status = _SEH2_GetExceptionCode();
1267     }
1268     _SEH2_END;
1269 
1270 cleanup:
1271     UserLeave();
1272 
1273     return Status;
1274 }
1275 
1276 /* EOF */
1277