67de9c926a9aea295781e40fce0fad0a6a88c990
[reactos.git] / reactos / win32ss / gdi / ntgdi / dcobjs.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Functions for creation and destruction of DCs
5 * FILE: subsystems/win32/win32k/objects/dcobjs.c
6 * PROGRAMER: Timo Kreuzer (timo.kreuzer@rectos.org)
7 */
8
9 #include <win32k.h>
10
11 #define NDEBUG
12 #include <debug.h>
13
14 VOID
15 FASTCALL
16 DC_vUpdateFillBrush(PDC pdc)
17 {
18 PDC_ATTR pdcattr = pdc->pdcattr;
19 PBRUSH pbrFill;
20
21 /* Check if the brush handle has changed */
22 if (pdcattr->hbrush != pdc->dclevel.pbrFill->BaseObject.hHmgr)
23 {
24 /* Try to lock the new brush */
25 pbrFill = BRUSH_ShareLockBrush(pdcattr->hbrush);
26 if (pbrFill)
27 {
28 /* Unlock old brush, set new brush */
29 BRUSH_ShareUnlockBrush(pdc->dclevel.pbrFill);
30 pdc->dclevel.pbrFill = pbrFill;
31
32 /* Mark eboFill as dirty */
33 pdcattr->ulDirty_ |= DIRTY_FILL;
34 }
35 else
36 {
37 /* Invalid brush handle, restore old one */
38 pdcattr->hbrush = pdc->dclevel.pbrFill->BaseObject.hHmgr;
39 }
40 }
41
42 /* Check if the EBRUSHOBJ needs update */
43 if (pdcattr->ulDirty_ & DIRTY_FILL)
44 {
45 /* Update eboFill */
46 EBRUSHOBJ_vUpdateFromDC(&pdc->eboFill, pdc->dclevel.pbrFill, pdc);
47 }
48
49 /* Check for DC brush */
50 if (pdcattr->hbrush == StockObjects[DC_BRUSH])
51 {
52 /* ROS HACK, should use surf xlate */
53 /* Update the eboFill's solid color */
54 EBRUSHOBJ_vSetSolidRGBColor(&pdc->eboFill, pdcattr->crPenClr);
55 }
56
57 /* Clear flags */
58 pdcattr->ulDirty_ &= ~(DIRTY_FILL | DC_BRUSH_DIRTY);
59 }
60
61 VOID
62 FASTCALL
63 DC_vUpdateLineBrush(PDC pdc)
64 {
65 PDC_ATTR pdcattr = pdc->pdcattr;
66 PBRUSH pbrLine;
67
68 /* Check if the pen handle has changed */
69 if (pdcattr->hpen != pdc->dclevel.pbrLine->BaseObject.hHmgr)
70 {
71 /* Try to lock the new pen */
72 pbrLine = PEN_ShareLockPen(pdcattr->hpen);
73 if (pbrLine)
74 {
75 /* Unlock old brush, set new brush */
76 BRUSH_ShareUnlockBrush(pdc->dclevel.pbrLine);
77 pdc->dclevel.pbrLine = pbrLine;
78
79 /* Mark eboLine as dirty */
80 pdcattr->ulDirty_ |= DIRTY_LINE;
81 }
82 else
83 {
84 /* Invalid pen handle, restore old one */
85 pdcattr->hpen = pdc->dclevel.pbrLine->BaseObject.hHmgr;
86 }
87 }
88
89 /* Check if the EBRUSHOBJ needs update */
90 if (pdcattr->ulDirty_ & DIRTY_LINE)
91 {
92 /* Update eboLine */
93 EBRUSHOBJ_vUpdateFromDC(&pdc->eboLine, pdc->dclevel.pbrLine, pdc);
94 }
95
96 /* Check for DC pen */
97 if (pdcattr->hpen == StockObjects[DC_PEN])
98 {
99 /* Update the eboLine's solid color */
100 EBRUSHOBJ_vSetSolidRGBColor(&pdc->eboLine, pdcattr->crPenClr);
101 }
102
103 /* Clear flags */
104 pdcattr->ulDirty_ &= ~(DIRTY_LINE | DC_PEN_DIRTY);
105 }
106
107 VOID
108 FASTCALL
109 DC_vUpdateTextBrush(PDC pdc)
110 {
111 PDC_ATTR pdcattr = pdc->pdcattr;
112
113 /* Timo : The text brush should never be changed.
114 * Jérôme : Yeah, but its palette must be updated anyway! */
115 if(pdcattr->ulDirty_ & DIRTY_TEXT)
116 EBRUSHOBJ_vUpdateFromDC(&pdc->eboText, pbrDefaultBrush, pdc);
117
118 /* Update the eboText's solid color */
119 EBRUSHOBJ_vSetSolidRGBColor(&pdc->eboText, pdcattr->crForegroundClr);
120
121 /* Clear flag */
122 pdcattr->ulDirty_ &= ~DIRTY_TEXT;
123 }
124
125 VOID
126 FASTCALL
127 DC_vUpdateBackgroundBrush(PDC pdc)
128 {
129 PDC_ATTR pdcattr = pdc->pdcattr;
130
131 if(pdcattr->ulDirty_ & DIRTY_BACKGROUND)
132 EBRUSHOBJ_vUpdateFromDC(&pdc->eboBackground, pbrDefaultBrush, pdc);
133
134 /* Update the eboBackground's solid color */
135 EBRUSHOBJ_vSetSolidRGBColor(&pdc->eboBackground, pdcattr->crBackgroundClr);
136
137 /* Clear flag */
138 pdcattr->ulDirty_ &= ~DIRTY_BACKGROUND;
139 }
140
141 VOID
142 NTAPI
143 DC_vSetBrushOrigin(PDC pdc, LONG x, LONG y)
144 {
145 /* Set the brush origin */
146 pdc->dclevel.ptlBrushOrigin.x = x;
147 pdc->dclevel.ptlBrushOrigin.y = y;
148
149 /* Set the fill origin */
150 pdc->ptlFillOrigin.x = x + pdc->ptlDCOrig.x;
151 pdc->ptlFillOrigin.y = y + pdc->ptlDCOrig.y;
152 }
153
154 /**
155 * \name NtGdiSetBrushOrg
156 *
157 * \brief Sets the brush origin that GDI uses when drawing with pattern
158 * brushes. The brush origin is relative to the DC origin.
159 *
160 * @implemented
161 */
162 _Success_(return != FALSE)
163 BOOL
164 APIENTRY
165 NtGdiSetBrushOrg(
166 _In_ HDC hdc,
167 _In_ INT x,
168 _In_ INT y,
169 _Out_opt_ LPPOINT pptOut)
170 {
171 PDC pdc;
172
173 /* Lock the DC */
174 pdc = DC_LockDc(hdc);
175 if (pdc == NULL)
176 {
177 EngSetLastError(ERROR_INVALID_HANDLE);
178 return FALSE;
179 }
180
181 /* Check if the old origin was requested */
182 if (pptOut != NULL)
183 {
184 /* Enter SEH for buffer transfer */
185 _SEH2_TRY
186 {
187 /* Probe and copy the old origin */
188 ProbeForWrite(pptOut, sizeof(POINT), 1);
189 *pptOut = pdc->pdcattr->ptlBrushOrigin;
190 }
191 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
192 {
193 DC_UnlockDc(pdc);
194 _SEH2_YIELD(return FALSE);
195 }
196 _SEH2_END;
197 }
198
199 /* Call the internal function */
200 DC_vSetBrushOrigin(pdc, x, y);
201
202 /* Unlock the DC and return success */
203 DC_UnlockDc(pdc);
204 return TRUE;
205 }
206
207 HPALETTE
208 NTAPI
209 GdiSelectPalette(
210 HDC hDC,
211 HPALETTE hpal,
212 BOOL ForceBackground)
213 {
214 PDC pdc;
215 HPALETTE oldPal = NULL;
216 PPALETTE ppal;
217
218 // FIXME: Mark the palette as a [fore\back]ground pal
219 pdc = DC_LockDc(hDC);
220 if (!pdc)
221 {
222 return NULL;
223 }
224
225 /* Check if this is a valid palette handle */
226 ppal = PALETTE_ShareLockPalette(hpal);
227 if (!ppal)
228 {
229 DC_UnlockDc(pdc);
230 return NULL;
231 }
232
233 /* Is this a valid palette for this depth? */
234 if ((!pdc->dclevel.pSurface) ||
235 (BitsPerFormat(pdc->dclevel.pSurface->SurfObj.iBitmapFormat) <= 8
236 && (ppal->flFlags & PAL_INDEXED)) ||
237 (BitsPerFormat(pdc->dclevel.pSurface->SurfObj.iBitmapFormat) > 8))
238 {
239 /* Get old palette, set new one */
240 oldPal = pdc->dclevel.hpal;
241 pdc->dclevel.hpal = hpal;
242 DC_vSelectPalette(pdc, ppal);
243
244 /* Mark the brushes invalid */
245 pdc->pdcattr->ulDirty_ |= DIRTY_FILL | DIRTY_LINE |
246 DIRTY_BACKGROUND | DIRTY_TEXT;
247 }
248
249 if(pdc->dctype == DCTYPE_MEMORY)
250 {
251 // This didn't work anyway
252 //IntGdiRealizePalette(hDC);
253 }
254
255 PALETTE_ShareUnlockPalette(ppal);
256 DC_UnlockDc(pdc);
257
258 return oldPal;
259 }
260
261 /*
262 * @implemented
263 */
264 HBRUSH
265 APIENTRY
266 NtGdiSelectBrush(
267 IN HDC hDC,
268 IN HBRUSH hBrush)
269 {
270 PDC pDC;
271 HBRUSH hOrgBrush;
272
273 if (hDC == NULL || hBrush == NULL) return NULL;
274
275 pDC = DC_LockDc(hDC);
276 if (!pDC)
277 {
278 return NULL;
279 }
280
281 /* Simply return the user mode value, without checking */
282 hOrgBrush = pDC->pdcattr->hbrush;
283 pDC->pdcattr->hbrush = hBrush;
284 DC_vUpdateFillBrush(pDC);
285
286 DC_UnlockDc(pDC);
287
288 return hOrgBrush;
289 }
290
291 /*
292 * @implemented
293 */
294 HPEN
295 APIENTRY
296 NtGdiSelectPen(
297 IN HDC hDC,
298 IN HPEN hPen)
299 {
300 PDC pDC;
301 HPEN hOrgPen;
302
303 if (hDC == NULL || hPen == NULL) return NULL;
304
305 pDC = DC_LockDc(hDC);
306 if (!pDC)
307 {
308 return NULL;
309 }
310
311 /* Simply return the user mode value, without checking */
312 hOrgPen = pDC->pdcattr->hpen;
313 pDC->pdcattr->hpen = hPen;
314 DC_vUpdateLineBrush(pDC);
315
316 DC_UnlockDc(pDC);
317
318 return hOrgPen;
319 }
320
321 /*
322 * @implemented
323 */
324 HBITMAP
325 APIENTRY
326 NtGdiSelectBitmap(
327 IN HDC hdc,
328 IN HBITMAP hbmp)
329 {
330 PDC pdc;
331 HBITMAP hbmpOld;
332 PSURFACE psurfNew, psurfOld;
333 PREGION VisRgn;
334 HDC hdcOld;
335 ULONG cBitsPixel;
336 ASSERT_NOGDILOCKS();
337
338 /* Verify parameters */
339 if (hdc == NULL || hbmp == NULL) return NULL;
340
341 /* First lock the DC */
342 pdc = DC_LockDc(hdc);
343 if (!pdc)
344 {
345 return NULL;
346 }
347
348 /* Must be a memory dc to select a bitmap */
349 if (pdc->dctype != DC_TYPE_MEMORY)
350 {
351 DC_UnlockDc(pdc);
352 return NULL;
353 }
354
355 /* Save the old bitmap */
356 psurfOld = pdc->dclevel.pSurface;
357
358 /* Check if there is a bitmap selected */
359 if (psurfOld)
360 {
361 /* Get the old bitmap's handle */
362 hbmpOld = psurfOld->BaseObject.hHmgr;
363 }
364 else
365 {
366 /* Use the default bitmap */
367 hbmpOld = StockObjects[DEFAULT_BITMAP];
368 }
369
370 /* Check if the new bitmap is already selected */
371 if (hbmp == hbmpOld)
372 {
373 /* Unlock the DC and return the old bitmap */
374 DC_UnlockDc(pdc);
375 return hbmpOld;
376 }
377
378 /* Check if the default bitmap was passed */
379 if (hbmp == StockObjects[DEFAULT_BITMAP])
380 {
381 psurfNew = NULL;
382
383 /* Default bitmap is 1x1 pixel */
384 pdc->dclevel.sizl.cx = 1;
385 pdc->dclevel.sizl.cy = 1;
386 }
387 else
388 {
389 /* Reference the new bitmap and check if it's valid */
390 psurfNew = SURFACE_ShareLockSurface(hbmp);
391 if (!psurfNew)
392 {
393 DC_UnlockDc(pdc);
394 return NULL;
395 }
396
397 /* Check if the bitmap is compatile with the dc */
398 cBitsPixel = gajBitsPerFormat[psurfNew->SurfObj.iBitmapFormat];
399 if ((cBitsPixel != 1) &&
400 (cBitsPixel != pdc->ppdev->gdiinfo.cBitsPixel) &&
401 (psurfNew->hSecure == NULL))
402 {
403 /* Dereference the bitmap, unlock the DC and fail. */
404 SURFACE_ShareUnlockSurface(psurfNew);
405 DC_UnlockDc(pdc);
406 return NULL;
407 }
408
409 /* Set the bitmap's hdc and check if it was set before */
410 hdcOld = InterlockedCompareExchangePointer((PVOID*)&psurfNew->hdc, hdc, 0);
411 if (hdcOld != NULL)
412 {
413 /* The bitmap is already selected into a different DC */
414 ASSERT(hdcOld != hdc);
415
416 /* Dereference the bitmap, unlock the DC and fail. */
417 SURFACE_ShareUnlockSurface(psurfNew);
418 DC_UnlockDc(pdc);
419 return NULL;
420 }
421
422 /* Copy the bitmap size */
423 pdc->dclevel.sizl = psurfNew->SurfObj.sizlBitmap;
424
425 /* Check if the bitmap is a dibsection */
426 if (psurfNew->hSecure)
427 {
428 /* Set DIBSECTION attribute */
429 pdc->pdcattr->ulDirty_ |= DC_DIBSECTION;
430 }
431 else
432 {
433 /* Remove DIBSECTION attribute */
434 pdc->pdcattr->ulDirty_ &= ~DC_DIBSECTION;
435 }
436 }
437
438 /* Select the new bitmap */
439 pdc->dclevel.pSurface = psurfNew;
440
441 /* Check if there was a bitmap selected before */
442 if (psurfOld)
443 {
444 /* Reset hdc of the old bitmap, it isn't selected anymore */
445 psurfOld->hdc = NULL;
446
447 /* Dereference the old bitmap */
448 SURFACE_ShareUnlockSurface(psurfOld);
449 }
450
451 /* Mark the dc brushes invalid */
452 pdc->pdcattr->ulDirty_ |= DIRTY_FILL | DIRTY_LINE;
453
454 /* FIXME: Improve by using a region without a handle and selecting it */
455 VisRgn = IntSysCreateRectpRgn( 0,
456 0,
457 pdc->dclevel.sizl.cx,
458 pdc->dclevel.sizl.cy);
459
460 if (VisRgn)
461 {
462 GdiSelectVisRgn(hdc, VisRgn);
463 REGION_Delete(VisRgn);
464 }
465
466 /* Unlock the DC */
467 DC_UnlockDc(pdc);
468
469 /* Return the old bitmap handle */
470 return hbmpOld;
471 }
472
473
474 BOOL
475 APIENTRY
476 NtGdiSelectClipPath(
477 HDC hDC,
478 int Mode)
479 {
480 HRGN hrgnPath;
481 PPATH pPath;
482 BOOL success = FALSE;
483 PDC_ATTR pdcattr;
484 PDC pdc;
485
486 pdc = DC_LockDc(hDC);
487 if (!pdc)
488 {
489 EngSetLastError(ERROR_INVALID_PARAMETER);
490 return FALSE;
491 }
492 pdcattr = pdc->pdcattr;
493
494 pPath = PATH_LockPath(pdc->dclevel.hPath);
495 if (!pPath)
496 {
497 DC_UnlockDc(pdc);
498 return FALSE;
499 }
500
501 /* Check that path is closed */
502 if (pPath->state != PATH_Closed)
503 {
504 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
505 DC_UnlockDc(pdc);
506 return FALSE;
507 }
508
509 /* Construct a region from the path */
510 else if (PATH_PathToRegion(pPath, pdcattr->jFillMode, &hrgnPath))
511 {
512 PREGION prgnPath = REGION_LockRgn(hrgnPath);
513 ASSERT(prgnPath);
514 success = IntGdiExtSelectClipRgn(pdc, prgnPath, Mode) != ERROR;
515 REGION_UnlockRgn(prgnPath);
516 GreDeleteObject( hrgnPath );
517
518 /* Empty the path */
519 if (success)
520 PATH_EmptyPath(pPath);
521
522 /* FIXME: Should this function delete the path even if it failed? */
523 }
524
525 PATH_UnlockPath(pPath);
526 DC_UnlockDc(pdc);
527
528 return success;
529 }
530
531 HFONT
532 NTAPI
533 DC_hSelectFont(
534 _In_ PDC pdc,
535 _In_ HFONT hlfntNew)
536 {
537 PLFONT plfntNew;
538 HFONT hlfntOld;
539
540 // Legacy crap that will die with font engine rewrite
541 if (!NT_SUCCESS(TextIntRealizeFont(hlfntNew, NULL)))
542 {
543 return NULL;
544 }
545
546 /* Get the current selected font */
547 hlfntOld = pdc->dclevel.plfnt->BaseObject.hHmgr;
548
549 /* Check if a new font should be selected */
550 if (hlfntNew != hlfntOld)
551 {
552 /* Lock the new font */
553 plfntNew = LFONT_ShareLockFont(hlfntNew);
554 if (plfntNew)
555 {
556 /* Success, dereference the old font */
557 LFONT_ShareUnlockFont(pdc->dclevel.plfnt);
558
559 /* Select the new font */
560 pdc->dclevel.plfnt = plfntNew;
561 pdc->pdcattr->hlfntNew = hlfntNew;
562
563 /* Update dirty flags */
564 pdc->pdcattr->ulDirty_ |= DIRTY_CHARSET;
565 pdc->pdcattr->ulDirty_ &= ~SLOW_WIDTHS;
566 }
567 else
568 {
569 /* Failed, restore old, return NULL */
570 pdc->pdcattr->hlfntNew = hlfntOld;
571 hlfntOld = NULL;
572 }
573 }
574
575 return hlfntOld;
576 }
577
578 HFONT
579 APIENTRY
580 NtGdiSelectFont(
581 _In_ HDC hdc,
582 _In_ HFONT hfont)
583 {
584 HFONT hfontOld;
585 PDC pdc;
586
587 /* Check parameters */
588 if ((hdc == NULL) || (hfont == NULL))
589 {
590 return NULL;
591 }
592
593 /* Lock the DC */
594 pdc = DC_LockDc(hdc);
595 if (!pdc)
596 {
597 return NULL;
598 }
599
600 /* Call the internal function */
601 hfontOld = DC_hSelectFont(pdc, hfont);
602
603 /* Unlock the DC */
604 DC_UnlockDc(pdc);
605
606 /* Return the previously selected font */
607 return hfontOld;
608 }
609
610 HANDLE
611 APIENTRY
612 NtGdiGetDCObject(HDC hDC, INT ObjectType)
613 {
614 HGDIOBJ SelObject;
615 DC *pdc;
616 PDC_ATTR pdcattr;
617
618 /* From Wine: GetCurrentObject does not SetLastError() on a null object */
619 if(!hDC) return NULL;
620
621 if(!(pdc = DC_LockDc(hDC)))
622 {
623 return NULL;
624 }
625 pdcattr = pdc->pdcattr;
626
627 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
628 DC_vUpdateFillBrush(pdc);
629
630 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
631 DC_vUpdateLineBrush(pdc);
632
633 switch(ObjectType)
634 {
635 case GDI_OBJECT_TYPE_EXTPEN:
636 case GDI_OBJECT_TYPE_PEN:
637 SelObject = pdcattr->hpen;
638 break;
639
640 case GDI_OBJECT_TYPE_BRUSH:
641 SelObject = pdcattr->hbrush;
642 break;
643
644 case GDI_OBJECT_TYPE_PALETTE:
645 SelObject = pdc->dclevel.hpal;
646 break;
647
648 case GDI_OBJECT_TYPE_FONT:
649 SelObject = pdcattr->hlfntNew;
650 break;
651
652 case GDI_OBJECT_TYPE_BITMAP:
653 {
654 SURFACE *psurf = pdc->dclevel.pSurface;
655 SelObject = psurf ? psurf->BaseObject.hHmgr : StockObjects[DEFAULT_BITMAP];
656 break;
657 }
658
659 case GDI_OBJECT_TYPE_COLORSPACE:
660 DPRINT1("FIXME: NtGdiGetCurrentObject() ObjectType OBJ_COLORSPACE not supported yet!\n");
661 // SelObject = dc->dclevel.pColorSpace.BaseObject.hHmgr; ?
662 SelObject = NULL;
663 break;
664
665 default:
666 SelObject = NULL;
667 EngSetLastError(ERROR_INVALID_PARAMETER);
668 break;
669 }
670
671 DC_UnlockDc(pdc);
672 return SelObject;
673 }
674
675 /* See WINE, MSDN, OSR and Feng Yuan - Windows Graphics Programming Win32 GDI and DirectDraw
676 *
677 * 1st: http://www.codeproject.com/gdi/cliprgnguide.asp is wrong!
678 *
679 * The intersection of the clip with the meta region is not Rao it's API!
680 * Go back and read 7.2 Clipping pages 418-19:
681 * Rao = API & Vis:
682 * 1) The Rao region is the intersection of the API region and the system region,
683 * named after the Microsoft engineer who initially proposed it.
684 * 2) The Rao region can be calculated from the API region and the system region.
685 *
686 * API:
687 * API region is the intersection of the meta region and the clipping region,
688 * clearly named after the fact that it is controlled by GDI API calls.
689 */
690 INT
691 APIENTRY
692 NtGdiGetRandomRgn(
693 HDC hdc,
694 HRGN hrgnDest,
695 INT iCode)
696 {
697 INT ret = 0;
698 PDC pdc;
699 PREGION prgnSrc = NULL;
700
701 pdc = DC_LockDc(hdc);
702 if (!pdc)
703 {
704 EngSetLastError(ERROR_INVALID_HANDLE);
705 return -1;
706 }
707
708 switch (iCode)
709 {
710 case CLIPRGN:
711 prgnSrc = pdc->dclevel.prgnClip;
712 break;
713
714 case METARGN:
715 prgnSrc = pdc->dclevel.prgnMeta;
716 break;
717
718 case APIRGN:
719 if (pdc->fs & DC_FLAG_DIRTY_RAO)
720 CLIPPING_UpdateGCRegion(pdc);
721 if (pdc->prgnAPI)
722 {
723 prgnSrc = pdc->prgnAPI;
724 }
725 else if (pdc->dclevel.prgnClip)
726 {
727 prgnSrc = pdc->dclevel.prgnClip;
728 }
729 else if (pdc->dclevel.prgnMeta)
730 {
731 prgnSrc = pdc->dclevel.prgnMeta;
732 }
733 break;
734
735 case SYSRGN:
736 prgnSrc = pdc->prgnVis;
737 break;
738
739 default:
740 break;
741 }
742
743 if (prgnSrc)
744 {
745 PREGION prgnDest = REGION_LockRgn(hrgnDest);
746 if (prgnDest)
747 {
748 ret = IntGdiCombineRgn(prgnDest, prgnSrc, 0, RGN_COPY) == ERROR ? -1 : 1;
749 if ((ret == 1) && (iCode == SYSRGN))
750 IntGdiOffsetRgn(prgnDest, pdc->ptlDCOrig.x, pdc->ptlDCOrig.y);
751 REGION_UnlockRgn(prgnDest);
752 }
753 else
754 ret = -1;
755 }
756
757 DC_UnlockDc(pdc);
758
759 return ret;
760 }
761
762 ULONG
763 APIENTRY
764 NtGdiEnumObjects(
765 IN HDC hdc,
766 IN INT iObjectType,
767 IN ULONG cjBuf,
768 OUT OPTIONAL PVOID pvBuf)
769 {
770 UNIMPLEMENTED;
771 return 0;
772 }
773
774 /* EOF */