summaryrefslogtreecommitdiffstats
path: root/private/ole2ui32/drawicon.cpp
blob: 1fd01514b4d2f27dea4ea38411aff2da30748ddf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
/*
 * DRAWICON.CPP
 *
 * Functions to handle creation of metafiles with icons and labels
 * as well as functions to draw such metafiles with or without the label.
 *
 * The metafile is created with a comment that marks the records containing
 * the label code.  Drawing the metafile enumerates the records, draws
 * all records up to that point, then decides to either skip the label
 * or draw it.
 *
 * Copyright (c)1992 Microsoft Corporation, All Right Reserved
 */

#include "precomp.h"
#include "utility.h"
#include "malloc.h"

// Private implementation

//Structure for label and source extraction from a metafile
typedef struct tagLABELEXTRACT
{
        LPTSTR      lpsz;
        UINT        Index;      // index in lpsz (so we can retrieve 2+ lines)
        DWORD       PrevIndex;  // index of last line (so we can mimic word wrap)

        union
                {
                UINT    cch;        //Length of label for label extraction
                UINT    iIcon;      //Index of icon in source extraction.
                } u;

        //For internal use in enum procs
        BOOL        fFoundIconOnly;
        BOOL        fFoundSource;
        BOOL        fFoundIndex;
} LABELEXTRACT, FAR * LPLABELEXTRACT;


//Structure for extracting icons from a metafile (CreateIcon parameters)
typedef struct tagICONEXTRACT
{
        HICON       hIcon;          //Icon created in the enumeration proc.

        /*
         * Since we want to handle multitasking well we have the caller
         * of the enumeration proc instantiate these variables instead of
         * using statics in the enum proc (which would be bad).
         */
        BOOL        fAND;
        HGLOBAL     hMemAND;        //Enumeration proc allocates and copies
} ICONEXTRACT, FAR * LPICONEXTRACT;


//Structure to use to pass info to EnumMetafileDraw
typedef struct tagDRAWINFO
{
        RECT     Rect;
        BOOL     fIconOnly;
} DRAWINFO, FAR * LPDRAWINFO;


int CALLBACK EnumMetafileIconDraw(HDC, HANDLETABLE FAR *, METARECORD FAR *, int, LPARAM);
int CALLBACK EnumMetafileExtractLabel(HDC, HANDLETABLE FAR *, METARECORD FAR *, int, LPLABELEXTRACT);
int CALLBACK EnumMetafileExtractIcon(HDC, HANDLETABLE FAR *, METARECORD FAR *, int, LPICONEXTRACT);
int CALLBACK EnumMetafileExtractIconSource(HDC, HANDLETABLE FAR *, METARECORD FAR *, int, LPLABELEXTRACT);

/*
 * Strings for metafile comments.  KEEP THESE IN SYNC WITH THE
 * STRINGS IN GETICON.CPP
 */

static const char szIconOnly[] = "IconOnly"; // Where to stop to exclude label.

/*
 * OleUIMetafilePictIconFree
 *
 * Purpose:
 *  Deletes the metafile contained in a METAFILEPICT structure and
 *  frees the memory for the structure itself.
 *
 * Parameters:
 *  hMetaPict       HGLOBAL metafilepict structure created in
 *                  OleMetafilePictFromIconAndLabel
 *
 * Return Value:
 *  None
 */

STDAPI_(void) OleUIMetafilePictIconFree(HGLOBAL hMetaPict)
{
        if (NULL != hMetaPict)
        {
                STGMEDIUM stgMedium;
                stgMedium.tymed = TYMED_MFPICT;
                stgMedium.hMetaFilePict = hMetaPict;
                stgMedium.pUnkForRelease = NULL;
                ReleaseStgMedium(&stgMedium);
        }
}

/*
 * OleUIMetafilePictIconDraw
 *
 * Purpose:
 *  Draws the metafile from OleMetafilePictFromIconAndLabel, either with
 *  the label or without.
 *
 * Parameters:
 *  hDC             HDC on which to draw.
 *  pRect           LPRECT in which to draw the metafile.
 *  hMetaPict       HGLOBAL to the METAFILEPICT from
 *                  OleMetafilePictFromIconAndLabel
 *  fIconOnly       BOOL specifying to draw the label or not.
 *
 * Return Value:
 *  BOOL            TRUE if the function is successful, FALSE if the
 *                  given metafilepict is invalid.
 */

STDAPI_(BOOL) OleUIMetafilePictIconDraw(HDC hDC, LPCRECT pRect,
        HGLOBAL hMetaPict, BOOL fIconOnly)
{
        if (NULL == hMetaPict)
                return FALSE;

        LPMETAFILEPICT pMF = (LPMETAFILEPICT)GlobalLock(hMetaPict);

        if (NULL == pMF)
                return FALSE;

        DRAWINFO di;
        di.Rect = *pRect;
        di.fIconOnly = fIconOnly;

        //Transform to back to pixels
        int cx = XformWidthInHimetricToPixels(hDC, pMF->xExt);
        int cy = XformHeightInHimetricToPixels(hDC, pMF->yExt);

        SaveDC(hDC);

        SetMapMode(hDC, pMF->mm);
        SetViewportOrgEx(hDC, (pRect->right - cx) / 2, 0, NULL);
        SetViewportExtEx(hDC, min ((pRect->right - cx) / 2 + cx, cx), cy, NULL);

        if (fIconOnly)
                EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileIconDraw, (LPARAM)&di);
        else
           PlayMetaFile(hDC, pMF->hMF);

        RestoreDC(hDC, -1);

        GlobalUnlock(hMetaPict);
        return TRUE;
}

/*
 * EnumMetafileIconDraw
 *
 * Purpose:
 *  EnumMetaFile callback function that draws either the icon only or
 *  the icon and label depending on given flags.
 *
 * Parameters:
 *  hDC             HDC into which the metafile should be played.
 *  phTable         HANDLETABLE FAR * providing handles selected into the DC.
 *  pMFR            METARECORD FAR * giving the enumerated record.
 *  lParam          LPARAM flags passed in EnumMetaFile.
 *
 * Return Value:
 *  int             0 to stop enumeration, 1 to continue.
 */
int CALLBACK EnumMetafileIconDraw(HDC hDC, HANDLETABLE FAR *phTable,
        METARECORD FAR *pMFR, int cObj, LPARAM lParam)
{
        LPDRAWINFO lpdi = (LPDRAWINFO)lParam;

        /*
         * We play everything blindly except for DIBBITBLT (or DIBSTRETCHBLT)
         * and ESCAPE with MFCOMMENT.  For the BitBlts we change the x,y to
         * draw at (0,0) instead of wherever it was written to draw.  The
         * comment tells us there to stop if we don't want to draw the label.
         */

        //If we're playing icon only, stop enumeration at the comment.
        if (lpdi->fIconOnly)
        {
                if (META_ESCAPE==pMFR->rdFunction && MFCOMMENT==pMFR->rdParm[0])
                {
                        if (0 == lstrcmpiA(szIconOnly, (LPSTR)&pMFR->rdParm[2]))
                                return 0;
                }

                /*
                 * Check for the records in which we want to munge the coordinates.
                 * destX is offset 6 for BitBlt, offset 9 for StretchBlt, either of
                 * which may appear in the metafile.
                 */
                if (META_DIBBITBLT == pMFR->rdFunction)
                        pMFR->rdParm[6]=0;

                if (META_DIBSTRETCHBLT == pMFR->rdFunction)
                          pMFR->rdParm[9] = 0;
        }

        PlayMetaFileRecord(hDC, phTable, pMFR, cObj);
        return 1;
}


/*
 * OleUIMetafilePictExtractLabel
 *
 * Purpose:
 *  Retrieves the label string from metafile representation of an icon.
 *
 * Parameters:
 *  hMetaPict       HGLOBAL to the METAFILEPICT containing the metafile.
 *  lpszLabel       LPSTR in which to store the label.
 *  cchLabel        UINT length of lpszLabel.
 *  lpWrapIndex     DWORD index of first character in last line. Can be NULL
 *                  if calling function doesn't care about word wrap.
 *
 * Return Value:
 *  UINT            Number of characters copied.
 */
STDAPI_(UINT) OleUIMetafilePictExtractLabel(HGLOBAL hMetaPict, LPTSTR lpszLabel,
        UINT cchLabel, LPDWORD lpWrapIndex)
{
        if (NULL == hMetaPict || NULL == lpszLabel || 0 == cchLabel)
                return FALSE;

        /*
         * We extract the label by getting a screen DC and walking the metafile
         * records until we see the ExtTextOut record we put there.  That
         * record will have the string embedded in it which we then copy out.
         */
        LPMETAFILEPICT pMF = (LPMETAFILEPICT)GlobalLock(hMetaPict);

        if (NULL == pMF)
                return FALSE;

        LABELEXTRACT le;
        le.lpsz=lpszLabel;
        le.u.cch=cchLabel;
        le.Index=0;
        le.fFoundIconOnly=FALSE;
        le.fFoundSource=FALSE;  //Unused for this function.
        le.fFoundIndex=FALSE;   //Unused for this function.
        le.PrevIndex = 0;

        //Use a screen DC so we have something valid to pass in.
        HDC hDC = GetDC(NULL);
        EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileExtractLabel, (LONG)(LPLABELEXTRACT)&le);
        ReleaseDC(NULL, hDC);

        GlobalUnlock(hMetaPict);

        //Tell where we wrapped (if calling function cares)
        if (NULL != lpWrapIndex)
           *lpWrapIndex = le.PrevIndex;

        //Return amount of text copied
        return le.u.cch;
}

/*
 * EnumMetafileExtractLabel
 *
 * Purpose:
 *  EnumMetaFile callback function that walks a metafile looking for
 *  ExtTextOut, then concatenates the text from each one into a buffer
 *  in lParam.
 *
 * Parameters:
 *  hDC             HDC into which the metafile should be played.
 *  phTable         HANDLETABLE FAR * providing handles selected into the DC.
 *  pMFR            METARECORD FAR * giving the enumerated record.
 *  pLE             LPLABELEXTRACT providing the destination buffer and length.
 *
 * Return Value:
 *  int             0 to stop enumeration, 1 to continue.
 */

int CALLBACK EnumMetafileExtractLabel(HDC hDC, HANDLETABLE FAR *phTable,
        METARECORD FAR *pMFR, int cObj, LPLABELEXTRACT pLE)
{
        /*
         * We don't allow anything to happen until we see "IconOnly"
         * in an MFCOMMENT that is used to enable everything else.
         */
        if (!pLE->fFoundIconOnly)
        {
                if (META_ESCAPE == pMFR->rdFunction && MFCOMMENT == pMFR->rdParm[0])
                {
                        if (0 == lstrcmpiA(szIconOnly, (LPSTR)&pMFR->rdParm[2]))
                                pLE->fFoundIconOnly=TRUE;
                }
                return 1;
        }

        //Enumerate all records looking for META_EXTTEXTOUT - there can be more
        //than one.
        if (META_EXTTEXTOUT == pMFR->rdFunction)
        {
                /*
                 * If ExtTextOut has NULL fuOptions, then the rectangle is omitted
                 * from the record, and the string starts at rdParm[4].  If
                 * fuOptions is non-NULL, then the string starts at rdParm[8]
                 * (since the rectange takes up four WORDs in the array).  In
                 * both cases, the string continues for (rdParm[2]+1) >> 1
                 * words.  We just cast a pointer to rdParm[8] to an LPSTR and
                 * lstrcpyn into the buffer we were given.
                 *
                 * Note that we use element 8 in rdParm instead of 4 because we
                 * passed ETO_CLIPPED in for the options on ExtTextOut--docs say
                 * [4] which is rect doesn't exist if we passed zero there.
                 *
                 */

                UINT cchMax = min(pLE->u.cch - pLE->Index, (UINT)pMFR->rdParm[2]);
                LPTSTR lpszTemp = pLE->lpsz + pLE->Index;
#ifdef _UNICODE
                MultiByteToWideChar(CP_ACP, 0, (LPSTR)&pMFR->rdParm[8], cchMax,
                        lpszTemp, cchMax+1);
#else
                lstrcpyn(lpszTemp, (LPSTR)&pMFR->rdParm[8], cchMax+1);
#endif
                lpszTemp[cchMax+1] = 0;

                pLE->PrevIndex = pLE->Index;
                pLE->Index += cchMax;
        }
        return 1;
}

/*
 * OleUIMetafilePictExtractIcon
 *
 * Purpose:
 *  Retrieves the icon from metafile into which DrawIcon was done before.
 *
 * Parameters:
 *  hMetaPict       HGLOBAL to the METAFILEPICT containing the metafile.
 *
 * Return Value:
 *  HICON           Icon recreated from the data in the metafile.
 */
STDAPI_(HICON) OleUIMetafilePictExtractIcon(HGLOBAL hMetaPict)
{
        if (NULL == hMetaPict)
                return NULL;

        /*
         * We extract the label by getting a screen DC and walking the metafile
         * records until we see the ExtTextOut record we put there.  That
         * record will have the string embedded in it which we then copy out.
         */
        LPMETAFILEPICT pMF = (LPMETAFILEPICT)GlobalLock(hMetaPict);

        if (NULL == pMF)
                return FALSE;

        ICONEXTRACT ie;
        ie.fAND = TRUE;

        //Use a screen DC so we have something valid to pass in.
        HDC hDC=GetDC(NULL);
        EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileExtractIcon, (LONG)&ie);
        ReleaseDC(NULL, hDC);

        GlobalUnlock(hMetaPict);

        return ie.hIcon;
}

/*
 * EnumMetafileExtractIcon
 *
 * Purpose:
 *  EnumMetaFile callback function that walks a metafile looking for
 *  StretchBlt (3.1) and BitBlt (3.0) records.  We expect to see two
 *  of them, the first being the AND mask and the second being the XOR
 *  data.  We
 *  ExtTextOut, then copies the text into a buffer in lParam.
 *
 * Parameters:
 *  hDC             HDC into which the metafile should be played.
 *  phTable         HANDLETABLE FAR * providing handles selected into the DC.
 *  pMFR            METARECORD FAR * giving the enumerated record.
 *  pIE             LPICONEXTRACT providing the destination buffer and length.
 *
 * Return Value:
 *  int             0 to stop enumeration, 1 to continue.
 */

int CALLBACK EnumMetafileExtractIcon(HDC hDC, HANDLETABLE FAR *phTable,
        METARECORD FAR *pMFR, int cObj, LPICONEXTRACT pIE)
{
        //Continue enumeration if we don't see the records we want.
        if (META_DIBBITBLT != pMFR->rdFunction && META_DIBSTRETCHBLT != pMFR->rdFunction)
                return 1;

        UNALIGNED BITMAPINFO* lpBI;
        UINT uWidth, uHeight;
        /*
         * Windows 3.0 DrawIcon uses META_DIBBITBLT in whereas 3.1 uses
         * META_DIBSTRETCHBLT so we have to handle each case separately.
         */
        if (META_DIBBITBLT==pMFR->rdFunction)       //Win3.0
        {
                //Get dimensions and the BITMAPINFO struct.
                uHeight = pMFR->rdParm[1];
                uWidth = pMFR->rdParm[2];
                lpBI = (LPBITMAPINFO)&(pMFR->rdParm[8]);
        }

        if (META_DIBSTRETCHBLT == pMFR->rdFunction)   //Win3.1
        {
                //Get dimensions and the BITMAPINFO struct.
                uHeight = pMFR->rdParm[2];
                uWidth = pMFR->rdParm[3];
                lpBI = (LPBITMAPINFO)&(pMFR->rdParm[10]);
        }

        UNALIGNED BITMAPINFOHEADER* lpBH=(LPBITMAPINFOHEADER)&(lpBI->bmiHeader);

        //Pointer to the bits which follows the BITMAPINFO structure.
        LPBYTE lpbSrc=(LPBYTE)lpBI+sizeof(BITMAPINFOHEADER);

        //Add the length of the color table (if one exists)
        if (0 != lpBH->biClrUsed)
        {
                // If we have an explicit count of colors used, we
                // can find the offset to the data directly
                lpbSrc += (lpBH->biClrUsed*sizeof(RGBQUAD));
        }
        else if (lpBH->biCompression == BI_BITFIELDS)
        {
                // 16 or 32 bpp, indicated by BI_BITFIELDS in the compression
                // field, have 3 DWORD masks for adjusting subsequent
                // direct-color values, and no palette
                lpbSrc += 3 * sizeof(DWORD);
        }
        else
        {
                // In other cases, there is an array of RGBQUAD entries
                // equal to 2^(biBitCount) where biBitCount is the number
                // of bits per pixel.  The exception is 24 bpp bitmaps,
                // which have no color table and just use direct RGB values.
                lpbSrc += (lpBH->biBitCount == 24) ? 0 :
                        (1 << (lpBH->biBitCount)) * sizeof(RGBQUAD);
        }

        // copy into aligned stack space (since SetDIBits needs aligned data)
        size_t nSize = lpbSrc - (LPBYTE)lpBI;
        LPBITMAPINFO lpTemp = (LPBITMAPINFO)_alloca(nSize);
        memcpy(lpTemp, lpBI, nSize);

        /*
         * All the bits we have in lpbSrc are device-independent, so we
         * need to change them over to be device-dependent using SetDIBits.
         * Once we have a bitmap with the device-dependent bits, we can
         * GetBitmapBits to have buffers with the real data.
         *
         * For each pass we have to allocate memory for the bits.  We save
         * the memory for the mask between passes.
         */

        HBITMAP hBmp;

        //Use CreateBitmap for ANY monochrome bitmaps
        if (pIE->fAND || 1==lpBH->biBitCount)
                hBmp=CreateBitmap((UINT)lpBH->biWidth, (UINT)lpBH->biHeight, 1, 1, NULL);
        else
                hBmp=CreateCompatibleBitmap(hDC, (UINT)lpBH->biWidth, (UINT)lpBH->biHeight);

        if (!hBmp || !SetDIBits(hDC, hBmp, 0, (UINT)lpBH->biHeight, (LPVOID)lpbSrc, lpTemp, DIB_RGB_COLORS))
        {
                if (!pIE->fAND)
                        GlobalFree(pIE->hMemAND);

                DeleteObject(hBmp);
                return 0;
        }

        //Allocate memory and get the DDBits into it.
        BITMAP bm;
        GetObject(hBmp, sizeof(bm), &bm);

        DWORD cb = bm.bmHeight*bm.bmWidthBytes * bm.bmPlanes;
        HGLOBAL hMem = GlobalAlloc(GHND, cb);

        if (NULL==hMem)
        {
                if (NULL != pIE->hMemAND)
                        GlobalFree(pIE->hMemAND);

                DeleteObject(hBmp);
                return 0;
        }

        LPBYTE lpbDst = (LPBYTE)GlobalLock(hMem);
        GetBitmapBits(hBmp, cb, (LPVOID)lpbDst);
        DeleteObject(hBmp);
        GlobalUnlock(hMem);

        /*
         * If this is the first pass (pIE->fAND==TRUE) then save the memory
         * of the AND bits for the next pass.
         */
        if (pIE->fAND)
        {
                pIE->fAND = FALSE;
                pIE->hMemAND = hMem;

                //Continue enumeration looking for the next blt record.
                return 1;
        }
        else
        {
                //Get the AND pointer again.
                lpbSrc=(LPBYTE)GlobalLock(pIE->hMemAND);

                /*
                 * Create the icon now that we have all the data.  lpbDst already
                 * points to the XOR bits.
                 */

                int cxIcon = GetSystemMetrics(SM_CXICON);
                int cyIcon = GetSystemMetrics(SM_CYICON);

                pIE->hIcon = CreateIcon(_g_hOleStdInst, uWidth, uHeight,
                        (BYTE)bm.bmPlanes, (BYTE)bm.bmBitsPixel, lpbSrc, lpbDst);

                GlobalUnlock(pIE->hMemAND);
                GlobalFree(pIE->hMemAND);
                GlobalFree(hMem);

                return 0;
        }
}


/*
 * OleUIMetafilePictExtractIconSource
 *
 * Purpose:
 *  Retrieves the filename and index of the icon source from a metafile
 *  created with OleMetafilePictFromIconAndLabel.
 *
 * Parameters:
 *  hMetaPict       HGLOBAL to the METAFILEPICT containing the metafile.
 *  lpszSource      LPTSTR in which to store the source filename.  This
 *                  buffer should be MAX_PATH characters.
 *  piIcon          UINT FAR * in which to store the icon's index
 *                  within lpszSource
 *
 * Return Value:
 *  BOOL            TRUE if the records were found, FALSE otherwise.
 */
STDAPI_(BOOL) OleUIMetafilePictExtractIconSource(HGLOBAL hMetaPict,
        LPTSTR lpszSource, UINT FAR *piIcon)
{
        if (NULL == hMetaPict || NULL == lpszSource || NULL == piIcon)
                return FALSE;

        /*
         * We will walk the metafile looking for the two comment records
         * following the IconOnly comment.  The flags fFoundIconOnly and
         * fFoundSource indicate if we have found IconOnly and if we have
         * found the source comment already.
         */

        LPMETAFILEPICT pMF = (LPMETAFILEPICT)GlobalLock(hMetaPict);
        if (NULL == pMF)
                return FALSE;

        LABELEXTRACT    le;
        le.lpsz = lpszSource;
        le.fFoundIconOnly = FALSE;
        le.fFoundSource = FALSE;
        le.fFoundIndex = FALSE;

        //Use a screen DC so we have something valid to pass in.
        HDC hDC = GetDC(NULL);
        EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileExtractIconSource,
                (LONG)(LPLABELEXTRACT)&le);
        ReleaseDC(NULL, hDC);
        GlobalUnlock(hMetaPict);

        //Copy the icon index to the caller's variable.
        *piIcon=le.u.iIcon;

        //Check that we found everything.
        return (le.fFoundIconOnly && le.fFoundSource && le.fFoundIndex);
}


/*
 * EnumMetafileExtractIconSource
 *
 * Purpose:
 *  EnumMetaFile callback function that walks a metafile skipping the first
 *  comment record, extracting the source filename from the second, and
 *  the index of the icon in the third.
 *
 * Parameters:
 *  hDC             HDC into which the metafile should be played.
 *  phTable         HANDLETABLE FAR * providing handles selected into the DC.
 *  pMFR            METARECORD FAR * giving the enumerated record.
 *  pLE             LPLABELEXTRACT providing the destination buffer and
 *                  area to store the icon index.
 *
 * Return Value:
 *  int             0 to stop enumeration, 1 to continue.
 */

int CALLBACK EnumMetafileExtractIconSource(HDC hDC, HANDLETABLE FAR *phTable,
        METARECORD FAR *pMFR, int cObj, LPLABELEXTRACT pLE)
{
        /*
         * We don't allow anything to happen until we see "IconOnly"
         * in an MFCOMMENT that is used to enable everything else.
         */
        if (!pLE->fFoundIconOnly)
        {
                if (META_ESCAPE == pMFR->rdFunction && MFCOMMENT == pMFR->rdParm[0])
                {
                        if (0 == lstrcmpiA(szIconOnly, (LPSTR)&pMFR->rdParm[2]))
                                pLE->fFoundIconOnly=TRUE;
                }
                return 1;
        }

        //Now see if we find the source string.
        if (!pLE->fFoundSource)
        {
                if (META_ESCAPE == pMFR->rdFunction && MFCOMMENT == pMFR->rdParm[0])
                {
#ifdef _UNICODE
                        MultiByteToWideChar(CP_ACP, 0, (LPSTR)&pMFR->rdParm[2], -1,
                                pLE->lpsz, MAX_PATH);
#else
                        lstrcpyn(pLE->lpsz, (LPSTR)&pMFR->rdParm[2], MAX_PATH);
#endif
                        pLE->lpsz[MAX_PATH-1] = '\0';
                        pLE->fFoundSource=TRUE;
                }
                return 1;
        }

        //Next comment will be the icon index.
        if (META_ESCAPE == pMFR->rdFunction && MFCOMMENT == pMFR->rdParm[0])
        {
                /*
                 * This string contains the icon index in string form,
                 * so we need to convert back to a UINT.  After we see this
                 * we can stop the enumeration.  The comment will have
                 * a null terminator because we made sure to save it.
                 */
                LPSTR psz = (LPSTR)&pMFR->rdParm[2];
                pLE->u.iIcon = 0;

                //Do Ye Olde atoi
                while (*psz)
                        pLE->u.iIcon = (10*pLE->u.iIcon)+((*psz++)-'0');

                pLE->fFoundIndex=TRUE;
                return 0;
        }
        return 1;
}