Tuesday, April 03, 2007

A Righteous Way To Get The Fonts Directory

Some months ago, I had a bit of a problem where I had to write a program that went to the system fonts directory on a Windows machine. This is usually in "C:\Windows\Fonts" but on some machines the Windows directory can move.

Thus, after a bit of googling I came up with this code that worked consistently until this afternoon.

string result = System.Environment.ExpandEnvironmentVariables("%WinDir%");
result += "\\fonts\\";

This works because on Windows systems, you can find the WinDir environment variable set to the system directory. That quit working today when I encountered a weird interface problem with an ancient C++ program (written well over 10 years ago) that invokes a new, cool .NET program that uses the code above. The failure was an interesting one.

The old C++ code looks like this: (Maybe you can see what the problem was.)

loadParms.segEnv = 0;
loadParms.lpszCmdLine = commandLine;
loadParms.lpShow = show;
loadParms.lpReserved = NULL;

HINSTANCE hinst = LoadModule(exeName, &loadParms);

The loadParms struct has a segEnv handle that references the environment of the child process created by the LoadModule(). However, since it is not initialized to anything, the child process has NO environment defined. Thus the reference to %WinDir% gets nada instead of the correct directory. The newer program worked fine from the DOS command line and failed within the LoadModule() call above. This was the only clue to my problem.

I came into my boss's office chuckling about how the old system goofiness had screwed me over and pointed out the use of the %WinDir% environment variable. He countered that what I'd done above to use %WinDir% in the first place was not as righteous as using the various Environment.SpecialFolder methods/properties to get the same thing. I'd tried to do this months back and I'd failed to google up a better solution than the one using %WinDir%.

But I'd forgotten my earlier failure to find a more righteous solution. It took me a couple hours of googling to remember that I'd done this before without success. Happily, this time, I didn't give up and came upon the clues that yielded this snippet of code:

///
/// get system font path
///

/// system font path
static public string GetFontPath()
{
string systemPath = Environment.GetFolderPath(Environment.SpecialFolder.System);
string result = Path.GetDirectoryName(systemPath)
+ Path.DirectorySeparatorChar
+ "FONTS"
+ Path.DirectorySeparatorChar;
return result;
}//GetFontPath()

This works in two parts, the Environment.SpecialFolder.System gets me to C:\Windows\System32, which is too deep in the directory tree for me. But the 2nd step, invoking Path.GetDirectoryName(systemPath), strips off the "\System32" that is in my way. Then I can add back the FONTS myself.

A righteous hack. I hope this will make the next guy's googling a little easier.

1 comment:

Caleb said...

Thanks, I was thinking of doing something similar but thought I'd look to see if there was some other standard way that I was missing.