[NTOS:KE/x64] Handle NMI vs swapgs race condition
[reactos.git] / win32ss / user / ntuser / clipboard.c
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 */