Classic VB Corner

Honoring Hidden Fonts

Windows 7 hides a number of locale-inappropriate fonts by default, and allows users to toggle this property as well. Here's how to honor that setting.

Remember the old warnings about not loading too many fonts, as they'd slow the system down? Yeah, you probably ignored those too. And later regretted it, at times, as you scanned your font selection dialogs or dropdowns looking for a particular font. You'd probably also wonder, "what are some of those fonts?!?" Happened to me a lot.

Well, Windows 7 now helps, a bit, with this problem by offering what amounts to a Hidden property for fonts. By default, most locale-inappropriate fonts are set to hidden, and users may also toggle this property from the Fonts dialog in Control Panel. Unfortunately, Microsoft chose to not expose any API to this useful property.

This shouldn't be an issue, according to Microsoft, as the common font dialog hides these fonts for you. But if you want to expose a dropdown list of fonts for users to choose from, like, oh, Microsoft Word, well, tough luck. Hmmm, but looking at both Microsoft Word 2007 and 2010, running under Windows 7, and they are at least so far playing by their own rules, it appears. Pull up WordPad, though, and you'll quickly see that the dropdown font list in that applet is somehow detecting which fonts are hidden and which are not.

So how is WordPad making this determination? On a hunch, I fired up Process Monitor (procmon), and started watching the registry changes while I toggled the hidden property of a few fonts on and off. Bingo! It turns out there's a registry entry that holds a list of all the hidden fonts in Windows 7. You can find the "Inactive Fonts" entry under this key:

\HKCU\Software\Microsoft\Windows NT\CurrentVersion\Font Management

This "Inactive Fonts" entry is stored in the REG_MULTI_SZ format, which is just a series (or list) of null-terminated (vbNullChar) strings. This is probably the easiest list to search, of all, as we can leverage the power of VB's native Instr function like so:

Public Function FontHidden(ByVal FontFamily As String, _
  Optional ByVal ForceRefresh As Boolean = True) As Boolean
  
  Static Initialized As Boolean
  Static fnts As String
  Const Key As String = _
   "Software\Microsoft\Windows NT\CurrentVersion\Font Management"
  
  ' If this is the first time this routine has been called, or a
  ' forced refresh is requested, read hidden fonts from registry.
  If Initialized = False Or ForceRefresh = True Then
   fnts = RegGetStringValue _
     (HKEY_CURRENT_USER, Key, "Inactive Fonts")
   fnts = vbNullChar & fnts
   Initialized = True
  End If
  
  ' Registry string is REG_MULTI_SZ, which means it's nullchar
  ' delimited So we bracket search term with nullchars and do
  ' a case-insensitive scan:
  FontFamily = vbNullChar & Trim$(FontFamily) & vbNullChar
  FontHidden = CBool(InStr(1, fnts, FontFamily, vbTextCompare))
End Function

You can use the registry routine of your choice, of course, to snag that string from the system. The one I used will be available with the code download linked below. The function I wrote persists the registry value, rather than call it each time, on the assumption that it will be recalled repeatedly to filter a long list of fonts. But as you can see, I did provide a means to force a refresh of the list of hidden fonts.

The real meat of the FontHidden function is in the very last line where the return value is assigned. Note that when the list of hidden fonts is retrieved, I add a vbNullChar to the beginning of the string. Now, to search the list for a given substring, we simply bracket the search phrase with vbNullChar's and pass both to Instr. To be safe, I chose the case-insensitive vbTextCompare option. If Instr returns a value other than 0, the search string we're looking for matches an element in the list.

You can now call EnumFontFamiliesEx, and filter the results of that to remove all the hidden fonts by comparing the family name passed in each callback to the "Invalid Fonts" list stored in the registry. You can download the FontFilter sample from my Web site, for a completely coded example, if you'd like.

As a final note, I'll add that you may also want to filter out the fonts that begin with an "@" symbol, as these denote vertical fonts (see Figure 10) that are most typically used in Asian systems. It remains a mystery to me why Microsoft chose to return an attribute of the font by altering the font name, in this case, rather than through some identifiable data element. I'm also unsure why one particular font family, "@Kozuka", isn't typically filtered out by the common font dialog, while all others are. If you have any insight on either of those issues, please leave a comment below or drop me an email.

About the Author

Karl E. Peterson wrote Q&A, Programming Techniques, and various other columns for VBPJ and VSM from 1995 onward, until Classic VB columns were dropped entirely in favor of other languages. Similarly, Karl was a Microsoft BASIC MVP from 1994 through 2005, until such community contributions were no longer deemed valuable. He is the author of VisualStudioMagazine.com's new Classic VB Corner column. You can contact him through his Web site if you'd like to suggest future topics for this column.

comments powered by Disqus

Featured

  • Top 3 Blazor Extensions for Visual Studio Code

    Some developers prefer to create applications with Microsoft's open-source Blazor tooling from within the open-source, cross-platform Visual Studio Code editor. Here are the top tools in the VS Code Marketplace for those folk, as measured by the number of installations.

  • How to Invert a Machine Learning Matrix Using C#

    VSM Senior Technical Editor Dr. James McCaffrey, of Microsoft Research, explains why inverting a matrix -- one of the more common tasks in data science and machine learning -- is difficult and presents code that you can use as-is, or as a starting point for custom matrix inversion scenarios.

  • Microsoft Engineer: 'It's Time to Move OData to .NET 5'

    Microsoft engineer Sam Xu says "it’s time to move OData to .NET 5" and in a new blog post he shows how to do just that.

  • Microsoft Goes Virtual with Developer Education in Face of COVID-19

    Like many organizations that host developer educational events, Microsoft has gone virtual amid shelter-in-place directives and a surge in remote work stemming from the COVID-19 pandemic.

  • Microsoft Enhances Low-Code Power Apps

    Microsoft's nod to the low-code movement, Power Apps, has been enhanced with a bevy of new features, including mixed reality, canvas/model support in a new mobile app, UX improvements and more.

.NET Insight

Sign up for our newsletter.

Terms and Privacy Policy consent

I agree to this site's Privacy Policy.

Upcoming Events