INFO: Example of calling EnumFontFamilies from a DLL

Last reviewed: September 29, 1997
Article ID: Q98577
The information in this article applies to:

- Standard and Professional Editions of Microsoft Visual Basic programming

  system for Windows, versions 2.0, 3.0

SUMMARY

This article demonstrates how to obtain a list of available fonts for a device by calling EnumFontFamilies or EnumFonts from a DLL.

Visual Basic already provides a Fonts property for obtaining a list of available font names for a device. Microsoft recommends that you use the Fonts property instead of the function provided in this article to obtain a list of available fonts. Use the technique shown in this article only if you have encountered a bug or limitation when using the Fonts property.

To create the example shown below, you need a C compiler capable of creating Windows dynamic link libraries (DLLs), and you need to have the Visual Basic Control Development Kit (CDK) version 2.0 or 3.0. The CDK is provided with the Professional Edition of Visual Basic version 2.0 and 3.0 for Windows.

MORE INFORMATION

Below are the steps necessary to create a sample DLL that demonstrates using EnumFontFamilies:

STEP ONE: Create Example .DLL File

  1. Create a source file called FONTNAME.C and add the following code:

          #include <windows.h>
          #include <vbapi.h>
          #include <string.h>
    

          int FAR PASCAL _export EnumFontNames (HDC, HAD);
          int FAR PASCAL  _export GetNextFont (LPLOGFONT, LPNEWTEXTMETRIC,
    
                                               int, LPARAM);
          BOOL Win31OrGreater (VOID);
       
          int giFontCount;
          float gfVersion;
       
          //================================================================
          // Title
          //      EnumFontNames()
          //
          // Parameters
          //      hdc      Device context for which fonts will be enumerated
          //      had      Handle to Visual Basic string array where the
          //               font names will be placed.
          //
          // Returns
          //      The number of fonts enumerated.
          //================================================================
          int FAR PASCAL EnumFontNames (HDC hdc, HAD had)
          {
             giFontCount = 0;
       
             if ( Win31OrGreater() )
                //Use EnumFontFamilies under Win 3.1 and later
                while (EnumFontFamilies(hdc, NULL, GetNextFont, had));
             else
                //Need to use EnumFonts under Win 3.0
                while (EnumFonts(hdc, NULL, GetNextFont, had));
       
             return giFontCount;
          }
       
          //================================================================
          // Title
          //      GetNextFont()
          //
          // Parameters
          //      lplf     Far pointer to LOGFONT structure
          //      lpntm    Far pointer to NEWTEXTMETRIC structure
          //      FontType Type of font
          //      lp       User-defined.  In this case it holds the handle
          //               to a Visual Basic string array.
          //
          // Returns
          //      TRUE as a signal to enumerate the next font
          //      FALSE as a signal to stop enumeration
          //================================================================
          int FAR PASCAL GetNextFont
          (
             LPLOGFONT lplf,
             LPNEWTEXTMETRIC lpntm,
             int FontType,
             LPARAM lp
          )
          {
             static char szFirstFont[LF_FACESIZE + 1];
             char szFaceName[LF_FACESIZE + 1];
             int iElements, lbound;
       
             HAD had = (HAD) lp;
             LONG lBounds = VBArrayBounds(had, 1);
       
             //Get out if there are no elements in the array
             if (lBounds == AB_INVALIDINDEX)
                return FALSE;
       
             // Store the lower bound of the array for index 1
             lbound = LOBOUND(lBounds);
       
             //Get number of elements in the array
             iElements = HIBOUND(lBounds) - lbound + 1;
       
             //Initialize the vars holding the font face names
             if (giFontCount == 0)
                 szFirstFont[0] = '\0';
       
             szFaceName[0] = '\0';
       
             if (giFontCount <= iElements)
             {
                HLSTR hlstr;
                SHORT indexes[1];
       
                //Copy the face size into a buffer so that we can ensure its
                //null terminated
                if ( Win31OrGreater() )
                    lstrcpyn((LPSTR) szFaceName, lplf->lfFaceName,
                             LF_FACESIZE - 1);
                else
                    //Need to use C runtime routine fmemcpy instead of
                    //lstrcpyn under Win 3.0
                   _fmemcpy((LPVOID) szFaceName, lplf->lfFaceName,
                             LF_FACESIZE - 1);
       
                szFaceName[LF_FACESIZE] = '\0';
       
                if (giFontCount == 0)
       
                   //Store the first font retrieved.  If we see this font
                   //again, we know we've enumerated all the fonts
                   lstrcpy((LPSTR) szFirstFont, szFaceName);
       
                else if (!lstrcmp(szFirstFont, szFaceName))
                //If we see the same face name again, get out and stop
                //enumerating
                   return FALSE;
       
                //Assume a single index array
                indexes[0] = lbound + giFontCount;
       
                //Get the VB string handle from the VB array
                hlstr = VBArrayElement(had, VBArrayIndexCount(had),
                                       indexes);
       
                //Make sure the string handle is valid
                if (HIWORD(hlstr))
                {
                   //Add the fontname to the array
                   VBSetHlstr(&hlstr, (LPSTR) szFaceName, lstrlen((LPSTR)
                              szFaceName));
       
                   //Return and get the next font
                   giFontCount++;
                }
       
                return TRUE;
             }
       
             else
                //Can't fit all font names into the array provided, so get
                //out.
                return FALSE;
          }
       
          //================================================================
          // Title
          //      Win31OrGreater ()
          //
          // Returns
          //      TRUE if we're running under Windows 3.1 or better
          //      FALSE if we're running under Windows 3.0
          //================================================================
          BOOL Win31OrGreater ( VOID )
          {
              DWORD dVersion;
       
              //Check which version of Windows we're running under
              dVersion = GetVersion();
              if (LOBYTE(LOWORD(dVersion)) > 3 || (LOBYTE(LOWORD(dVersion))
                  == 3 && HIBYTE(LOWORD(dVersion)) > 0))
                  return TRUE;
              else
                  return FALSE;
          }
       
          //----------------------------------------------------------------
          // Initialize library. This routine is called when the first
          // client loads
          // the DLL.
          //----------------------------------------------------------------
          int FAR PASCAL LibMain
          (
             HANDLE hModule,
             WORD   wDataSeg,
             WORD   cbHeapSize,
             LPSTR  lpszCmdLine
          )
          {
             // Avoid warnings on unused (but required) formal parameters
             wDataSeg = wDataSeg;
             cbHeapSize = cbHeapSize;
             lpszCmdLine = lpszCmdLine;
       
             return 1;
          }
       
          //----------------------------------------------------------------
          // WEP
          //----------------------------------------------------------------
          int FAR PASCAL WEP(int fSystemExit);
       
          //----------------------------------------------------------------
          // Performs cleanup tasks when the DLL is unloaded.  WEP() is
          // called automatically by Windows when the DLL is unloaded (no
          // remaining tasks still have the DLL loaded).  It is strongly
          // recommended that a DLL have a WEP() function, even if it does
          // nothing but returns success (1), as in this example.
          //----------------------------------------------------------------
          int FAR PASCAL WEP
          (
              int fSystemExit
          )
          {
              // Avoid warnings on unused (but required) formal parameters
              fSystemExit = fSystemExit;
       
              return 1;
          }
    
    

  2. Create a module-definition file (DEF) called FONTNAME.DEF and add the following:

          LIBRARY FONTNAME
    

          DESCRIPTION 'Example of how to enumerate all font names for
    
                        specific device'
       
          EXETYPE WINDOWS
       
          CODE PRELOAD MOVEABLE DISCARDABLE
          DATA PRELOAD MOVEABLE SINGLE
       
          EXPORTS
             WEP @1 RESIDENTNAME
             ENUMFONTNAMES @2
             GETNEXTFONT @3
    
    

  3. Compile FONTNAME.C from the MS-DOS command line as follows:

          CL /c /ASw /W3 FONTNAME.C
    

  4. Link the resulting FONTNAME.OBJ file as follows:

          LINK /NOE /NOD
    
             FONTNAME.OBJ+LIBENTRY.OBJ,FONTNAME.DLL,,
             LIBW+SDLLCEW+VBAPI.LIB,FONTNAME.DEF;
    
    

  5. Resource compile FONTNAME.DLL to make it Windows 3.0 compatible as follows:

          RC /30 FONTNAME.DLL
    

  6. Copy FONTNAME.DLL to the \WINDOWS\SYSTEM directory.

STEP TWO: Create Visual Basic Sample Program

  1. Start Visual Basic or from the File menu, choose New Project (ALT, F, N) if Visual Basic is already running. Form1 is created by default.

  2. Add a list box (List1) to Form1.

  3. Add the following Declare statement as one, single line to the General Declarations section of Form1:

          Declare Function EnumFontNames Lib "FONTNAME.DLL" (ByVal hDC As
    
             Integer, FontNames() As String) As Integer
    
    

  4. Add the following code to the Form_Click event of Form1:

          Sub Form_Click ()
       
             Dim i As Integer
             Dim FontCount As Integer
             ReDim FontNames(255) As String  'Make the array intentionally
                                             'large to hold any number of
                                             'font names
       
             'For Screen fonts, pass Form1.hDC instead.  If using the
             'Common Dialog control, you can also pass the hDC property
             'of the Common Dialog control.
             FontCount = EnumFontNames(Printer.hDC, FontNames())
       
             List1.Clear
             For i = 0 To FontCount - 1
                List1.AddItem FontNames(i)
             Next
       
          End Sub
    
    

  5. From the Run menu, choose Start (ALT, R, S) or press the F5 key to run the program.

  6. Click Form1. The available font names for the selected printer will be displayed in the list box.
Keywords          : APrgOther vbwin kbfasttip
Technology        : kbvba
Version           : WINDOWS:2.0 3.0
Platform          : WINDOWS
Issue type        : kbinfo


================================================================================


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: September 29, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.