Using Metafiles with Visual Basic

A Metafile is a set of drawing commands that can be stored either in a 
disk file or a memory location and can be played on either the screen in 
a picture control or form or on the printer.

Visual Basic itself has no statements for creating or using metafiles so 
it is necessary to use calls to the Windows API.  If you've never used 
API functions, you need to get Daniel Appleman's "Visual Basic 
Programmer's Guide to the Windows API."  This book covers all of the 
functions you will see in the GRAPHMF project.

Metafiles have several uses.  You can create metafiles of charts or 
drawings and then save them to disk for later playback.  You can also 
create a "Print Preview," copy them to the clipboard where they can be 
pasted into other Windows applications and be displayed or printed just 
as they were originally displayed in your VB app, and you can print or 
display them in any size without the distortion you get as you do with 
stretching bitmaps.


API Functions Vs. Visual Basic Statements
One of the great things about VB is the amount of time it saves you to 
perform what would normally take several lines of complex code.  For 
example, to print a 100 by 100 blue filled box at position 10, 10 in a 
picture control with VB requires only one statement:

Picture1.Line(10, 10) - (110, 110), QBColor(1), BF

To perform the same thing using Windows API Calls requires the following 
code:

Const BS_SOLID = 0

Declare Function CreatePen% Lib "GDI" (ByVal nPenStyle%, ByVal nWidth%, 
ByVal crColor&)
Declare Function CreateSolidBrush% Lib "GDI" (ByVal crColor&)
Declare Function DeleteObject% Lib "GDI" (ByVal hObject%)
Declare Function Rectangle% Lib "GDI" (ByVal hDC%, ByVal X1%, ByVal Y1%, 
ByVal X2%, ByVal Y2%)
Declare Function SelectObject% Lib "GDI" (ByVal hDC%, ByVal hObject%)


Dim OldPen%, NewPen%, OldBrush%, NewBrush%, Clr&, PicDC%, Ret%

Clr& = QBColor(1)
NewPen% = CreatePen(BS_SOLID, 1, Clr)
NewBrush% = CreateSolidBrush(Clr)
OldPen% = SelectObject(PicDC%, NewPen%)
OldBrush% = SelectObject(PicDC%, NewBrush%)
Ret% = Rectangle(PicDC%, 10, 10, 110, 110)
Ret% = SelectObject(PicDC%, OldPen%)
Ret% = SelectObject(PicDC%, OldBrush%)
Ret% = DeleteObject(NewPen%)
Ret% = DeleteObject(NewBrush%)

As you can see, using API calls is much more complicated, but you can 
create your own functions to simplify things.  You could put the above 
code in a function as follows:

Sub DrawRect(PicDC%, Clr&, Tp%, Lft%, Wdth%, Hght%)
   Dim OldPen%, NewPen%, OldBrush%, NewBrush%, Ret%

   NewPen% = CreatePen(BS_SOLID, 1, Clr)
   NewBrush% = CreateSolidBrush(Clr)
   OldPen% = SelectObject(PicDC%, NewPen%)
   OldBrush% = SelectObject(PicDC%, NewBrush%)
   Ret% = Rectangle(PicDC%, Lft%, Tp%, Lft% + Wdth%, Tp% + Hght%)
   Ret% = SelectObject(PicDC%, OldPen%)
   Ret% = SelectObject(PicDC%, OldBrush%)
   Ret% = DeleteObject(NewPen%)
   Ret% = DeleteObject(NewBrush%)
End Sub

Then just call it with:
PicDC% = Picture1.hDC
Clr& = QBColor(1)
DrawRect PicDC%, Clr&, 10, 10, 100, 100

The SelectObject function may not be familiar to you.  Before you can 
use any pen or brush on a device, you need to select it into the device 
with SelectObject.  The return value of SelectObject is the handle to 
the old pen (or any object) that was associated with the device.  
Whenever you create a new pen, you need to delete it with DeleteObject 
when you are done with it; otherwise, you will run out of system 
resources.  Make sure you restore the original object or some other new 
object before you destroy any that you created.  Setting the ForeColor 
or FillColor properties on a control with VB does all of this for you 
automatically.

The GRAPHMF Project shows how to perform several different kinds of 
drawing functions with Windows API functions.


Mapping Modes
The mapping mode tells Windows what scale you are going to use.  VB has 
the ScaleMode property to set the scale of a picture control or printer 
to several different modes (Picture1.ScaleMode = 3, for instance, sets 
the mapping mode to pixel); however, VB doesn't actually change the 
mapping mode of the control, it just maintains an internal conversion 
process.  As far as Windows knows, all VB controls are set to pixel 
(ScaleMode = 3 or MM_TEXT); therefore, no matter what ScaleMode you use 
with VB, All Windows API calls use pixels.  Rectangle(PicDC%, 10, 10, 
110, 110), for instance, draws a box 100 pixels by 100 pixels 10 pixels 
from the left and 10 pixels down from the upper left corner of PicDC% no 
matter what scalemode you have PicDC% set to; therefore, you need to 
convert whatever scalemode you are using to pixels before calling the 
API functions.  The easiest way to do this is to have conversion 
variables for the X and Y values as follows:

Suppose you are creating a bar graph with values that have a maximum X 
value of 10 and a maximum Y value of 100.  You could use 
Picture1.Scale(0, 0) - (10, 100).  Then if you have a X value of 3 and a 
Y value of 20 and you want to show a bar you could just use 
Picture1.Line(X - .5, 100) - (X + .5, 100 - Y), , B.  A one unit wide 
bar will be drawn centered 3 units over and drawn 20 units up on the 
picture control.  The following code shows how to convert between the 
scale you are using to pixels:

Dim X!, Y!, CX!, CY!, MaxX!, MaxY!, PicDC%, Ret%
Dim W%, H%

Picture1.ScaleMode = 3
W% = Picture1.ScaleWidth
H% = Picture1.ScaleHeight
MaxX! = 10
MaxY! = 100
X! = 3
Y! = 20
CX! = W% / MaxX!
CY! = H% / MaxY!
PicDC% = Picture1.hDC
Ret% = Rectangle(PicDC%, (X! - .5) * CX!, H%, (X! + .5) * CX!, H% - Y! * CY!)
Picture1.Refresh

In these examples, the scale is set so that 0, 0 is in the upper left 
corner and the X values increase to the right and the Y values increase 
down.  This is the standard way Windows (and VB) sets the mapping mode.  
You can reset them using SetWindowOrg and SetWindowExt but it is much 
more complicated.  You can also change the mapping mode of a VB control 
with SetMapMode to MM_ANISOTROPIC and then use any scale you want; 
however, if you are going to use a combination of VB statements and API 
calls on the same control, this can get very complicated.  Whenever you 
change the mapping mode (or the viewport or window extents) of a VB 
control, you need to save the original device context settings and 
restore them with SaveDC and RestoreDC.  Using any VB statements while 
the mapping mode or window extents are changed will guarantee you 
problems.

Once you have down the basics of using API calls, it is a relatively 
simple matter to use them to create metafiles.  Instead of using the API 
calls to draw on a picture control, you just pass the handle of a 
metafile to the calls instead.


Creating a Metafile
Creating a metafile is very simple, just use the following code:

Add the following statements to the declarations section:
  Const MM_ANISOTROPIC = 8
  Declare Function CreateMetaFile% Lib "GDI" (ByVal lpString&)
  Declare Function CloseMetafile% Lib "GDI" (ByVal hMF%)
  Declare Function SetWindowOrg& Lib "GDI" (ByVal hDC%, ByVal X%, ByVal Y%)
  Declare Function SetWindowExt& Lib "GDI" (ByVal hDC%, ByVal X%, ByVal Y%)

Dim CMF%, Metafile%, RetL&

CMF% = CreateMetafile(0) ' Memory metafile
RetL& = SetMapMode(CMF%, MM_ANISOTROPIC)
RetL& = SetWindowOrg(CMF%, 0, 0)
RetL& = SetWindowExt(CMF%, Picture1.ScaleWidth, ScaleHeight)

Then just pass CMF% to any of the API drawing functions (or your own 
functions) instead of a picture control or form hDC.  When you are done 
drawing on the metafile, use Metafile% = CloseMetafile(CMF%) to obtain 
the handle (Metafile%) for the metafile.  It is important to set the 
mapping mode, origin, and extents of the metafile before you perform any 
drawing functions on the metafile handle.  These values represent the 
default size and position for the metafile and for reasons that will 
become clear are set to the width and height in pixels of a picture 
control.  If you use MM_ANISOTROPIC for a mapping mode, the metafile can 
be resized to any size.

Drawing lines on metafiles is fairly simple, but text is a little more 
difficult.  You need to create a font and then select it into the 
metafile device context before you can use TextOut to draw the text.  
The main problem you have is figuring out what size to make the font and 
how to position it.  VB has the FontSize, TextWidth, and TextHeight 
properties that allow you to set the size and determine how wide or tall 
a particular font will be.  There is an equivalent API call, 
GetTextExtent, to tell you the width and height of a text string on a 
given device; however, it doesn't work on a metafile device context 
(most of the other API text functions don't work either).

One way to get around this is to use a known size, set the metafile 
extents to that size, and then use the text point size, heights and 
widths that fit that size.  For instance, to determine a point size to 
set the font to for a metafile, you can use the following:

Windows uses twips as a standard measurement.  There are 1440 twips per 
inch.  If the screen resolution is set to 640 x 480, there will be 15 
twips per pixel.  There are 72 points in an inch and at 15 twips per 
pixel, there will be 0.75 points per pixel.  if you want a font size of 
12 points on the screen, setting the lfHeight element of TEXTMETRIC to -
Fix(12 / 0.75) will give you the same size font as the VB statement 
FontSize = 12 on a screen with a resolution of 640 x 480.

The text height can be determined by checking the TextHeight property 
for a VB control when the ScaleMode is set to Pixel (3).  For instance, 
the text height of MS Sans Serif at point size 12 is 19 pixels.  If you 
want to center text vertically, subtract 10 from the Y coordinate where 
you want the text placed.

Determining the text width is more of a problem.  One thing you can do 
is set the extents of the metafile to the same size as the picture 
control or printer on which you want the metafile displayed and then set 
the font properties of that control to the ones you want the metafile to 
be and then check the TextWidth of that control for the string you want 
to use.  Make sure the control is set to ScaleMode = 3.  If you use a 
fixed pitch font, you can just use the text width of a single character 
times the length of the text to determine the width.

The important thing to remember is to set the window extents of the 
metafile to the same size as the size of the control you are using.  In 
the GRAPHMF project, the Pic_Graph control is 280 by 280 pixels and the 
window extents of the metafile is also set to 280 by 280.  When the 
metafile is resized, the font size will automatically change relative to 
the size of the metafile.  The larger you make the extents of the metafile,
the more resolution you will have.


Printing Metafiles
The one thing that makes the trouble of using API calls to create 
drawings instead of VB statements is that when you want to print 
drawings, you don't have to have a whole other set of statements to 
print them.

Once you have the metafile, it is a fairly simple matter to print it to 
the printer (or to another form or picture control for that matter).  To 
print a metafile, simply determine the area you want the image to be 
placed and play the metafile as follows:

Suppose you are using a scale of 80 columns by 66 rows on the printer 
object (Printer.Scale(0, 0) - (80, 66)) and you want the drawing to 
start at row 30 and column 20 and you want it to extend to row 60 and 
column 80.

Add the following statements to the declarations section:
  Const MM_ANISOTROPIC = 8
  Declare Function RestoreDC% Lib "GDI" (ByVal hDC%, ByVal nSavedDC%)
  Declare Function SaveDC% Lib "GDI" (ByVal hDC%)
  Declare Function SetViewportExt& Lib "GDI" (ByVal hDC%, ByVal X%, ByVal Y%)
  Declare Function SetViewportOrg& Lib "GDI" (ByVal hDC%, ByVal X%, ByVal Y%)

Dim Tp%, Lft%, Wdth%, Hght%, SavDC%, Ret%, RetL&

Printer.Scale(0, 0) - (80, 66)
Printer.CurrentX = 20
Printer.CurrentY = 30
Printer.Scalemode = 3
Tp% = Printer.CurrentY
Lft% = Printer.CurrentX
Printer.Scale(0, 0) - (80, 66)
Printer.CurrentX = 80
Printer.CurrentY = 60
Printer.Scalemode = 3
Hght% = Printer.CurrentY - Tp%
Wdth% = Printer.CurrentX - Lft%
SavDC% = SaveDC(Printer.hDC)
Ret% = SetMapMode(Printer.hDC, MM_ANISOTROPIC)
RetL& = SetViewportOrg(Printer.hDC, Lft%, Tp%)
RetL& = SetViewportExt(Printer.hDC, Wdth%, Hght%)
Ret% = PlayMetaFile(Printer.hDC, Metafile%)
Ret% = RestoreDC(Printer.hDC, SavDC%)
Printer.Scale(0, 0) - (80, 66)


The Viewport is the area on the printer that is going to contain the 
drawing.  SetViewportOrg sets the top and left positions and 
SetViewportExt sets the width and height from that position.  
SetViewportOrg and SetViewportExt need integer values in pixels and the 
easiest way to get them is to set the current print position with 
CurrentX and CurrentY and then use ScaleMode to change the scaling of 
the control to pixel and then read back the current print position with 
CurrentX and CurrentY (the current print position doesn't change when 
you change scale modes).

The important thing to remember is to use SaveDC to save the current 
device context information and RestoreDC to restore it.  Don't use any 
VB statements on the printer object between the time you use SaveDC and 
until after you have used RestoreDC.

Copying Metafiles To The Clipboard
Now that you have your metafile handle Metafile%, you might think that 
copying it to the Clipboard would be as simple as executing 
Clipboard.SetData Metafile%, CF_METAFILE, but you would be very wrong.

You can't use the Clipboard object to copy metafiles.  Instead you need 
to use the Clipboard APIs OpenClipboard, SetClipboardData, and 
CloseClipboard.  You also need to know that anything you copy to the 
clipboard no longer belongs to your application; therefore, you need to 
make copies of things you want on the clipboard.

Copying metafiles to the clipboard is different than copying bitmaps in 
that a bitmap handle can be passed directly to the clipboard with:

Ret% = SetClipboardData(BMPHandle%, CF_BITMAP)

To copy metafiles, you need to pass a handle to a METAFILEPICT structure 
which describes the metafile.  The METAFILEPICT structure has the 
following elements:

Type METAFILEPICT
   mm As Integer    ' This is the mapping mode 
   xExt As Integer  ' This is the default width of the metafile
   yExt As Integer  ' This is the default height of the metafile
   hMF As Integer   ' This is the handle to the copy of the metafile
End Type

You can also use xExt and yExt to specify an aspect ratio by giving them 
negative values.  

Using MM_ANISOTROPIC for a mapping mode allows the metafile to be 
stretched to any size.  The xExt and yExt can be set to any value.  Use 
CopyMetafile to make a copy of the metafile for hMf.  The following code 
puts this all together:

Add the following statements to the declarations section:
  Const MM_ANISOTROPIC = 8
  Declare Function CopyMetafileByNum% Lib "GDI" Alias "CopyMetaFile" 
(ByVal 
  hMF%, ByVal lpFilename&)

Dim MP As METAFILEPICT
Dim CpyMetafile%

MP.mm = MM_ANISOTROPIC
MP.xExt = 1200 ' Arbitrary setting
MP.yExt = 1200 ' Arbitrary setting
CpyMetafile% = CopyMetafileByNum(Metafile%, 0)
MP.hMF = CpyMetafile%

Now you have your METAFILEPICT all ready to go and all you have to do is 
copy it to the clipboard, right?  Except that the clipboard wants a 
handle to the memory block that contains the METAFILEPICT structure.  VB 
doesn't let you know what the address or handle to variables are so you 
have to make your own.  To do this you need to use the GlobalAlloc and 
GlobalLock functions as follows:

Add the following statements to the declarations section:
  Const GMEM_MOVEABLE = &H2
  Declare Function GlobalAlloc% Lib "Kernel" (ByVal wFlags%, ByVal dwBytes&)
  Declare Function GlobalLock& Lib "Kernel" (ByVal hMem%)
  Declare Function GlobalUnlock% Lib "Kernel" (ByVal hMem%)

Dim Memhndl%, MemAddr&

Memhndl% = GlobalAlloc(GMEM_MOVEABLE, Len(MP))
MemAddr& = GlobalLock(Memhndl%)

Now you have a handle and an address to a memory block, but your 
metafile information is stored in MP.  You need to copy the information 
in MP to the new memory location so you can pass it to the clipboard.  
To do this you use hmemcpy as follows:

Add the following statement to the declarations section:
  Declare Sub hmemcpy Lib "Kernel" (ByVal hpvDest&, hpvsource As METAFILEPICT,
  ByVal cbCopy&)

hmemcpy MemAddr&, MP
Ret% = GlobalUnlock(Memhndl%)

Finally you have everything you need to copy the metafile to the 
clipboard with the following statements:

Add the following statments to the declarations section:
  Const CF_METAFILEPICT = 3
  Declare Function CloseClipBoard% Lib "User" ()
  Declare Function OpenClipboard% Lib "User" (ByVal hWnd%)
  Declare Function SetClipBoardData% Lib "User" (ByVal wFormat%, ByVal hMem%)

Ret% = OpenClipBoard(Form1.hWnd)
Ret% = SetClipBoardData(CF_METAFILEPICT, MemHndl%)
Ret% = CloseClipboard()

Whatever you do, don't use GlobalFree to release Memhndl%, it no longer 
belongs to you and if you try to free it you will get a GPF.  You also 
don't need to use DeleteMetafile to release CpyMetafile%.

You probably want to add some error checking to make sure the clipboard 
is available (OpenClipboard returns zero if it is not available) and to 
make sure the metafile was copied (SetClipboardData will return the same 
value as Memhndl% on success).

To see the difference in appearance of a metafile vs. a bitmap, create a 
graph with GRAPHMF, open up another Windows application that accepts 
bitmaps and metafiles (like Word for Windows) and copy the bitmap with 
the Copy BMP button and insert it into the other application, then copy 
the metafile with the Copy MF button and insert it into the other 
application.  Stretch both so they are the same size and then print them 
out.


PRINTING AND VIEWING METAFILES FROM A DISK FILE
Windows metafiles are stored with the extension .WMF; however, there are 
two types of metafiles that Windows uses.  There are standard metafiles 
(which are the kind CopyMetafile creates when creating a disk file), and 
placeable metafiles.  Placeable metafiles have an additional METAFILEHEADER
structure at the beginning of the file that gives information about the 
size and location of the metafile.  The METAFILEHEADER structure as the
following elements:

Type METAFILEHEADER
   Key As Long          ' Should be the value &H9AC6CDD7&
   HMF As Integer       ' Not used, should be 0
   bbox As RECT         ' A RECT Structure, size and position of metafile
   inch As Integer      ' Number of metafile units per inch
   reserved As Long     ' Not used, should be 0
   checksum As Integer  ' Checksum
End Type

The bbox RECT structure contains the position on the page (or screen) where
the metafile is to be placed and the width and height of the metafile.  The
inch element contains the number of metafile units per inch.  To convert 
the metafile units to a size that will come out in the proper dimensions on
the screen or printer, use the following:

Suppose the RECT structure contains the following:

bbox.left =250
bbox.top = 500
bbox.right = 1250
bbox.bottom = 2000

and the inch value is 500, meaning that there are 500 metafile units per 
inch, it would mean that the metafile should be placed 1/2" from the left 
of the page (250 metafile units / 500 metafile units per inch = 0.5 inches)
and 1" from the top of the page.  The metafile is 2 inches wide (1250 - 250
/ 500), and the height is 1 1/2 inches.  If you are using a ScaleMode of 3 
(Pixel), you need to convert these inch values to pixel values.  
TwipsPerPixelX and TwipsPerPixelY allow you to convert pixel values to twips
and there are 1440 twips in an inch; therefore, to convert metafile units 
to pixel values use the following:

Pixels = Metafile Units / Metafile Units per Inch * 1440 / Screen(or 
Printer).TwipsPerPixelX

If the metafile is 1000 metafile units wide and there are 500 metafile units
per inch and there are 15 twips per pixel on the device you are using, then 
by replacing the variables in the above formula gives you 1000 / 500 * 1440
/ 15 = 192.  The metafile needs to be 192 pixels wide on the device to 
appear 2 inches wide.

Now that you know the size and position of the metafile, you can proceed 
with playing it.  Normally, you would use GetMetafile to obtain a handle to
a metafile, but if you try to use GetMetafile to read a placeable metafile,
you will discover that nothing happens.  This is because GetMetafile doesnt
work on placeable metafiles because the METAFILEHEADER structure is in the 
way.  So you need to read in the METAFILEHEADER structure from the file and 
then copy the rest of the metafile to a global memory block.  The following 
code shows you how to use GlobbalAlloc and lread to accomplish this as well 
as how to use GetMetafile for a standard metafile (the metafile DOS file 
name is assigned to File$):

Add the following statements to the declarations section (Type statements 
must be in a module):
  Const GMEM_MOVEABLE = &H2
  Const MM_ANISOTROPIC = 8 

  Type RECT
      left As Integer
      top As Integer
      right As Integer
      bottom As Integer
  End Type

  Type METAFILEHEADER
      Key As Long
      HMF As Integer
      bbox As RECT
      inch As Integer
      reserved As Long
      checksum As Integer
  End Type

  Declare Function GlobalAlloc% Lib "Kernel" (ByVal wFlags%, ByVal dwBytes&)
  Declare Function GlobalFree% Lib "Kernel" (ByVal hMem%)
  Declare Function GlobalLock& Lib "Kernel" (ByVal hMem%)
  Declare Function GlobalUnlock% Lib "Kernel" (ByVal hMem%)
  Declare Function lread% Lib "Kernel" Alias "_lread" (ByVal hFile%, ByVal *
  lpBuffer&, ByVal wBytes%)


Dim Ret%, RetL&, Fi%, File$, Tp%, Lft%, Wdth%, Hght%, hMF%, Gptr&, SavedDC%
Dim Bytelen&
Dim WMFH As METAFILEHEADER

Printer.ScaleMode = 3
SavedDC% = SaveDC(Printer.hDC)
File$ = "filename.wmf"
Fi% = FreeFile
Open File$ For Binary As #Fi%
Get #Fi%, , WMFH
If WMFH.key = &H9AC6CDD7 Then  ' If it is not this value, metafile is not 
                               '  placeable
   Wdth% = ((WMFH.bbox.right - WMFH.bbox.left) / WMFH.inch) * (1440 / *
   Printer.TwipsPerPixelX)
   Hght% = ((WMFH.bbox.bottom - WMFH.bbox.Top) / WMFH.inch) * (1440 / *
   Printer.TwipsPerPixelY)
   Tp% = ((WMFH.bbox.Top) / WMFH.inch) * (1440 / Printer.TwipsPerPixelX)
   Lft% = ((WMFH.bbox.left) / WMFH.inch) * (1440 / Printer.TwipsPerPixelY)
   Bytlen& = LOF(Fi%) - 22     ' METAFILEHEADER is 22 bytes long
   hMF% = GlobalAlloc(GMEM_MOVEABLE, Bytlen&)
   If hMF% <> 0 Then
      Gptr& = GlobalLock(hMF%)
      Ret% = lread(FileAttr(Fi%, 2), Gptr&, Bytlen&)
      Close Fi%
   End If
Else
   Close Fi%
   Wdth% = 1200                  ' Arbitrary width
   Hght% = 1200                  ' Arbitrary height
   Lft% = (Printer.ScaleWidth - Wdth%) / 2 ' Center horizontally on the page
   Tp% = (Printer.ScaleHeight - Hght%) / 2 ' Center vertically on the page
   hMF% = GetMetaFile(File$)
End If
Ret% = SetMapMode(Printer.hDC, MM_ANISOTROPIC)
RetL& = SetViewportOrg(Printer.hDC, Lft%, Tp%)
RetL& = SetViewportExt(Printer.hDC, Wdth%, Hght%)
Ret% = PlayMetaFile(Printer.hDC, hMF%)
If WMFH.key = &H9AC6CDD7 Then
   Ret% = GlobalUnlock(hMF%)
   Ret% = GlobalFree(hMF%)
Else
   Ret% = DeleteMetaFile(hMF%)
End If
Ret% = RestoreDC(Printer.hDC, SavedDC%)


You can also use the above code to play a metafile into a VB control; 
however, if you are not concerned about the size of the metafile, you can 
just use Picture1.Picture = LoadPicture("filename.wmf") which will work with
either kind of metafile.


Using a metafile as "Print Preview"
The great advantage of a metafile is that it will appear the same no 
matter what device you play it on.  If you use a metafile to create a 
printed report instead of VB statements, you can display the metafile on 
a form by playing it with the following statements:

Dim Ret, RetL&, ShDC%, SavedDC%

Form1.Autoredraw = True
Form1.Cls
Form1.ScaleMode = 3
ShDC% = Form1.hDC
SavedDC% = SaveDC(ShDC%)
Ret% = SetMapMode(ShDC%, MM_ANISOTROPIC)
RetL& = SetWindowOrg(ShDC%, 0, 0)
RetL& = SetWindowExt(ShDC%, Form1.ScaleWidth, Form1.ScaleHeight)
RetL& = SetViewportOrg(ShDC%, 0, 0)
RetL& = SetViewportExt(ShDC%, Form1.ScaleWidth, Form1.ScaleHeight)
Ret% = PlayMetaFile(ShDC%, Metafile%)
Ret% = RestoreDC(ShDC%, SavedDC%)
Form1.Refresh
Form1.Show


Questions or comments about the GRAPHMF project can be sent to Joe 
Oliphant.  E-Mail joe_oliphant@csufresno.edu or CompuServe 71742, 1451.
