Install Fonts For Logged On User via SCCM Package

June 12, 2011 — 45 Comments

When deploying a package that installs fonts (a simple MSI installation in our case) via SCCM, the user that is logged on to the computer when the MSI is installed will not be able to use these new fonts until they log out and log back on. Read on for my explanation and solution…

Note: I have only been working with Windows 7 machines (x86 & x64) so I have no idea if my solution works with XP or even if the same problem happens on XP in the first place, though I suspect it does.

 

The Problem

 

Let’s go through the exact symptoms of this problem first:

You have an MSI installation (or any other kind of installer) that installs an application and also installs some fonts. You run the silent installation manually to test it and find that the application gets installed correctly and when you launch the application you can see that the fonts have been correctly installed. However, when SCCM deploys this package and runs the exact same silent install you just tested but from the SCCM agent service, the application gets installed correctly but the fonts appear to be missing.

When the user launches the application that was just installed they can see that the fonts do not work as the application looks wrong and if the user looks in C:\Windows\Fonts the fonts are not there, despite the fact that the MSI installation log reported no errors for font installation and the fact that you can see the font files are there if you look via command prompt (or connect to the C$ share of the machine from another computer). All of the correct registry entries get created for the font just like they do when the installation is run manually, but the fonts just do not appear to be installed to the currently logged on user.

If the user logs off and logs back on though, the fonts are there and the application displays correctly. The fonts also work correctly straight away if no user was logged on during the SCCM deployment.

 

The Cause

 

So what is happening here? Well I have my own theory, which I have now pretty much proved, but have not been able to find any official confirmation anywhere that this is actually how it all works. My theory is as follows:

When a user logs on, Windows looks at each of the values in the following registry key:

HKLM\Software\Microsoft\Windows NT\CurrentVersion\Fonts

For each of the font files in this key, it calls the AddFontResource function. We can see this in action here in a Process Monitor capture of a user logging on:

image

At the start of the Process Monitor screenshot we can see winlogon is querying each value in that registry key mentioned above, then it starts to access font files corresponding to these values. Towards the bottom of the screenshot you can see it doing this, starting with C:\Windows\Fonts\arial.ttf, then ariali.ttf, then arialbd.ttf (and obviously it goes on and on for each font that was in the registry key).

Now if we open up one of the events in Process Monitor where winlogon is actually accessing one of the font files, we can view the call stack and see that it is indeed calling the AddFontResource function (ignore the Gdi prefix, that must just be the name of the internal function within gdi32.dll that AddFontResource actually calls)

Stack1

So at logon all existing fonts are available to the user’s session, then when they install a font (either manually or by running an installation that installs the fonts) the font gets added to that registry key so that it is available at next logon but AddFontResource also gets called there and then to make the font available straight away. One thing to note about the AddFontResource function is that the fonts it has added to the system font table get lost at logoff (due to something which I will cover in the next paragraph), which is presumably why it does not require administrator permissions and why that registry key is used to keep a list of all fonts that should be added back to the system font table when a user logs on.

So this is all well and good, but you might be thinking it is not getting us any closer to explaining why the fonts are not available to the current user when SCCM installs them. Well… we can see from the MSDN documentation that AddFontResource only makes a font available for applications in the same session that AddFontResource is being called from – this is the key point.  During logon this is fine because it is the winlogon.exe process that is calling AddFontResource and winlogon.exe is running from the user’s session. When a user installs a font themselves (manually or by running an installation program) it obviously is being run from their session. However, when SCCM tries to run the MSI installation that installs the fonts, it is being run from a service (the SCCM agent service) and therefore is in another session (in Vista/7 services all run in session 0, user sessions are session 1 or above).

So the fonts are getting installed in the exact same way when installed by SCCM (or indeed any service) – they get copied to C:\Windows\Fonts and added to the registry key mentioned earlier, and then AddFontResource gets called, its just that as this is being called from a different session to the user, the user does not get the fonts made available to their session. Of course when they log off and back on winlogon.exe will see the fonts in that registry key and call AddFontResource for them, so that is why the fonts work after logging off and back on (or logging on for the first time if SCCM installed the app while no one was logged on).

Oh and as for why the user cannot see the fonts in C:\Windows\Fonts when they are actually there, this is down to the hidden system file named desktop.ini that is used to make certain folders display files in a special way. If we use command prompt to go in and delete that desktop.ini file from C:\Windows\Fonts, and then go back into there in explorer we can see the new font files.

 

The Solution

 

So we have our goal then: we need to get AddFontResource to be called in the currently logged on user’s session instead of in the services session once the MSI installation is complete.

Obviously we cannot modify the SCCM agent service itself to tell it to do this, but we can make it run whatever program we want after the MSI installation is complete just by changing our advertised program in the SCCM package to be a batch file that first of all runs msiexec to install whatever app it is we are installing and then runs our chosen program once that has finished.

So I wrote a VB.NET program named StartInConsoleSession.exe that uses various Windows API functions to launch a specified process in the currently logged on user’s session (aka the console session) when run from the services session. Obviously this wouldn’t be any use in a Terminal Services environment where users don’t get logged on to the console session, but we would never be deploying apps to a TS server via SCCM so that doesn’t matter. The application I wrote is command line and has the following syntax:

StartInConsoleSession.exe [/W] cmdpath arguments

You can see an explanation of the arguments and examples in the screenshot below

image

Note that this operation of launching a process in the user’s session from another session requires permissions that only the Local System account has, so you will not be able to successfully run this unless it is being called from a service that is running as Local System – of course the SCCM agent service that will be launching our program meets these requirements so that is not a problem.

Anyway, back to our problem. So we now have a program that we can tell the SCCM agent to launch that will in turn launch a specified program in the user’s session. All we need now is a program that calls AddFontResource for specified font files so that we can make StartInConsoleSession.exe launch this in the user’s session.

So I wrote another VB.NET application: CurrentSessionFonts.exe (yeah I’m not very creative when I name my applications am I…). This is another command line program and it has the following syntax:

CurrentSessionFonts.exe [add | remove] fontfiles

So we just specify whether or not we want to add or remove a font, then specify the path to the font file. You can specify multiple font files if you separate them with a comma (no space between the font file names and the comma’s). e.g:

CurrentSessionFonts.exe add C:\Font1.fon,C:\Font2.ttf

NB: If your font file name has a comma in it… well… sorry, you better rename it (who puts commas in their file names anyway!).

Here is a call stack screenshot from my CurrentSessionFonts.exe program as it adds a font, as you can see the same function in gdi32.dll that winlogon.exe calls at logon is called.

Stack2

OK so we now have all the tools for the job, we just need to put them together – which we do by using the following batch file as our advertised program in SCCM (instead of just calling msiexec directly to install the MSI and do nothing more):

msiexec /i “%~dp0SomeInstall.msi” /qb!

“%~dp0StartInConsoleSession” /W “%~dp0CurrentSessionFonts” add %SystemRoot%\Fonts\Font1.TTF,%SystemRoot%\Fonts\Font2.FON

Ignore the word wrapping – that second part should be all on one line (you might want to copy and paste it into notepad to view it more clearly).

So obviously the first line just installs the MSI silently as normal, but what are we doing in the second line? Well firstly the %~dp0 variable is an environmental variable that expands to the location that the batch file is being run from. We can’t use %cd% because this package could be run from a UNC path and command prompt does not support the current directory (which is what %cd% is) being a UNC path. Oh and %systemroot% just expands to C:\Windows on the vast majority of PCs (the only time it wouldn’t is if you had not installed Windows on the C drive or something like that).

As you can see, we call StartInConsoleSession.exe and tell it to launch CurrentSessionFonts.exe in the user’s session. We pass in the “add” argument and the paths to the two font files we want to call AddFontResource for as arguments to the CurrentSessionFonts.exe program. We also use the /W argument of StartInConsoleSession.exe to tell it to wait for the process it is launching (in this case CurrentSessionFonts.exe) to exit before it returns control back to the batch file that it is being run from. In this example we don’t need to because we have nothing else after that line, but I figured other people might find this useful as you might not want your batch file to move on to the next line until the new process is finished doing its stuff and has terminated. Note that if you use /W then it must be the first argument passed to StartInConsoleSession.exe (otherwise it would just get passed in as an argument to whatever program you asked StartInConsoleSession to launch).

A more basic, and perhaps easier to read/understand, example would be:

msiexec.exe /i C:\SomeInstall.msi /qb!

C:\StartInConsoleSession.exe C:\CurrentSessionFonts.exe add C:\Windows\Fonts\Font1.FON

(again the second part should all be on one line)

but of course this would only work if your batch file copied those files to the root of C first (not advisable obviously) and is only provided here as an example of how to use the programs for anyone that didn’t understand that first example due to all the variables etc.

 

Downloads

 

You can download both of the programs I have written for this solution (see the section above titled “The Solution” for more information on how to use each one) from here:

StartInConsoleSession.zip

CurrentSessionFonts.zip

Note that both programs require .NET Framework v2.0, but as this is intended to be run on Windows 7 systems and they have .NET 2.0 and 3.5 installed straight out of the box, that shouldn’t be an issue.

Also note that they both require the file Cjwdev.WindowsApi.dll to be in the folder that they are being run from. Each zip folder contains a copy of this file but they are both identical so if you keep both EXEs in the same folder it doesn’t matter which version of the DLL you keep in that folder as well.

For any developers that would like to see exactly how all of this code works, send me an email (cwright@cjwdev.co.uk) and I will gladly send you the source code for both applications. Also, feel free to use my Windows API library in your own .NET projects – just download one of the ZIP files linked to above and pinch the Cjwdev.WindowsApi.dll file from it. Add a reference to this DLL from your own .NET project and you then have access to roughly 80 Windows API definitions and almost as many managed .NET methods that wrap these APIs into easy to use methods that you can use with absolutely no Windows API knowledge (all of the classes are in the Cjwdev.WindowsApi namespace). If you would like the intellisense descriptions/documentation to be displayed for the methods and classes in this DLL then see here.

 

The Conclusion

 

Never spend 3 full days (2 of those being weekend days!) on something as unimportant as users needing to log out and back on before they can use an application! 🙂

Just kidding… it has been quite an interesting experience, I’ve certainly learnt a lot from it. Plus its nice to know that I didn’t just settle for the old “it can’t be done” answer and actually ended up with a fully functional solution. Coming up with that solution was not as easy as it may have looked in this blog post (see my thread here for example) but it was still interesting.

As always, let me know if this helped you out or if you have any questions.

Chris

45 responses to Install Fonts For Logged On User via SCCM Package

  1. 

    A very interesting article, I was facing the exact same issue and was almost ready for “it can’t be done” and appending a reboot onto my job.

    Thanks for sharing this.

  2. 

    All of this is not necessary. All you to do is add the fonts to the Font table in the MSI. they show up in the font list no matter who installs it or who is logged in Msiexec handles everything from there.

    • 

      I’m afraid you are wrong and have not understood the problem. Do you really think I would have gone to all of this trouble if that was the case? Try it yourself if you don’t believe me – on a Windows 7 machine, install your fonts via MSI from a service that is running as Local System, and see if the currently logged on user can actually use those fonts without logging off and logging back on.

  3. 

    I already have.

    I don’t want to get into a confrontation with you. I used Wise to create the MSI, that maybe why It works for me. Also give the system a few minutes to refresh the file system. It may make the difference for you. I just tried this three times on two VMWare sessions and on live system running Windows 7 Pro.

    • 

      Trust me you’re not doing the same thing then. How are you installing your MSI? Just double clicking it? Running it from a logon script? Or are you actually deploying it via SCCM like we are? If you are deploying it via SCCM, how are you verifying that the fonts are actually installed for the user that is logged on whilst the installed took place? I’m sorry if I seemed confrontational, but its just that I’ve had several people thank me for solving this problem so I’m afraid I don’t believe that just installing the fonts in the normal way via MSI works… otherwise I wouldn’t have come up against this problem and neither would lots of other people.

  4. 

    Though SCCM running in the system account, trust me I have been packaging for over 15 years. I know the difference in a system account and just double clicking an Icon.

    • 

      OK fair enough, so how are you then confirming that the font is actually installed for the currently logged on user (without them logging off and logging back on) ?

  5. 

    Launch anything that makes use of Fonts, Word, Outlook, Acrobat…

    Look for the new fonts in the font list.

    • 

      and do they actually work though? Also just to clarify this when the installation happens while the user is logged on, not before they logon or anything. If you say it works fine for you then good I guess, but it certainly doesn’t for me and lots of other people and I’ve outlined the technical reasons why in this blog post so I can’t really see why it would be any different for you. Unless like you say the Wise installer does something special.

  6. 

    You wonderful!
    I had exactly same issue as you. (I made package for System Center Essentials to install Fonts to all computers in our domain. Fonts are successfully installed, however are not available for user who is logged on). I’ve searched with google and find your article. This solved problem. You have saved a lot of my work time. Thank you very much for writting and sharing this.

  7. 

    Hi, Is there any reason this wouldn’t work using Adobe .pfm fonts? It doesn’t seem to working for me. Thanks.

    • 

      I’ve never tried it with a PFM font (and never even heard of them to be honest) but if its a font like any other font file then it should work. What OS are you trying to run it on?

      • 

        Win7 enterprise x64. It’s an adobe post script font. The more I read about it, the more I don’t like it. Apparently there are two files needed for installation. The font file and a support file.

  8. 

    Hi Chris,

    I am curious why you and Michael had different conclusions on the same issue. I did some testing based on this issue (fonts don’t appear for the current user), but I haven’t tested through SCCM to install by Local system account.

    Based on my testing, I believe Michael is right. The “Font” table in MSI file makes
    difference. Two testing are performed as following:

    1. No entry in “Font” table within “example_font1.msi”
    Double-click on “example_font1.msi”
    Or run command msiexec /i example_font1.msi /qb in an elevated command prompt window
    Result: expected (installed) fonts don’t appear on Control Panel or C:\windows\fonts folder, but they appear in the registry.

    2. Added entries in Font table for each font file.
    Double-click on “example_font1.msi”
    Result: expected (installed) fonts do appear on Control Panel or C:\windows\fonts folder, also installed fonts appear in Word.

    I look forward to getting some feedback from you. Thank you for your posting.

    Sunshine

    different

    • 

      Yes that’s because you’re just double clicking the msi. If you were to install it from a service running as Local System (the SCCM service in my example) then you would get the same problem I and many others have got – which is what this article is about 🙂

      • 

        Thank you for your reply. Would you mind to look into your MSI file and check if the Font table has entries for each font file?

        I read the help about what Font table does. The Font table contains the information for registering font files with the system.

        • 

          Yes there was an entry in the font table for each font file (I even had to go in and manually edit the Font table because of the type of fonts they were – see here). I think you are missing the point of this problem and I’m guessing you haven’t read my explanation in this blog post of the cause of it. The font gets installed and made available instantly if I install the MSI normally (by double clicking it, or using msiexec etc) but that is because the font installation is running in the same session as me. When you install a font it is only made available for the session that it was installed from, and because services run in a different session then if you install a font from a service it is not made available to the logged on user until they log off and log back on. I explain the technical details of why this happens in this blog post so I’m not going to repeat it all again here.

  9. 
    LightTempler June 11, 2013 at 08:18

    MANY thx for great tool StartInConsoleSession.exe !
    I’m very interesseted in magic behind the scenes 😉
    Plz send me (as offered) a copy of the source code.
    (And my chiefs will feel much better before we use it
    on > 1300 machines)
    I’m on the Postscript Type 1 font install/uninstall trek, too.
    Hard to install / promote properly. But possible.
    Big … :-((( to uninstall. But on it.
    Thx&regards
    LiTe

  10. 

    Do they actually need to logout and log back in? The same is theoretically true for Active Setup, but you can force active setup to run just by stopping and restarting the explorer process (from task manager for example). I wonder whether the same would be true for the fonts issue?

  11. 

    Interesting solution (although there seems to be debate over whether there is a problem to solve!).

    I’ve not tried to reproduce the font issue myself but for anyone testing, you can install an MSI interactively as local system by using the Sysinternals PsExec tool with the -s -i switches.

    This StartInConsoleSession exe – it can be used to run any command as the logged in user like runas.exe but without the need to supply any credentials? Would it work on a terminal server with multiple users logged on?

    Also you could try a custom action to refresh the fonts that runs on self-repair triggered by a missing HKCU key.

    • 

      There definitely is a problem to be solved – otherwise I wouldn’t have spent 3 days coming up with that solution! 🙂 If it “just worked” like some people say then I wouldn’t have had the problem in the first place would I, and if my theory was wrong then my solution wouldn’t have fixed the problem (which it did).

      As for the StartInConsoleSession executable, it won’t work on a terminal server no – well it will work but it will launch the program in the console session (as the name implies), rather than in a specific user’s session. So if an admin is logged on at the physical screen, it would launch in their session.

  12. 

    Thanks a lot for your help!
    This is definitely a great solution to actually have the fonts installed on users’s computer after deploying the MSI package through SCCM.
    However when deploying to users’ computers, the StartInConsoleSession/CurrentSessionFonts is not silent, users do see a cmd screen come up and they do have the ability of closing the cmd.
    Is there a way to make it completely silent or not allow user’s to close the cmd window?

    Thanks, your assistance is greatly appreciated.

  13. 

    Thanks a lot!!!! You save me a lot of trouble.

  14. 

    Hi,

    I have the .tff files and the two files you made. I only have a batch file that installs it to C:\Windows\Fonts, but even when I made a MSI file to extract this to the right folder with the correct switch what you have above it times out (the 120 minute run time). I had to place it in a CMD file because it would end up being too long as I have 8 files.

    It works fine if I just run the MSI file normally.

    Is there a certain program you used to package the fonts together?

    Thanks!

    • 

      I would try running your batch file manually in the system context using PSEXEC so that you can see what is happening and what it is stalling on for so long on. It should be taking a few seconds, nowhere near 120 minutes.

  15. 

    Hello, should it work with .otf files? It doesn’t work for me.

    • 

      If they get installed in the same way as other fonts then yes it should work. Not sure what to suggest other than double checking you’ve done everything correctly as described above

  16. 

    I just did exactly what you did, spent 2 days trying to figure out why my package wasn’t working through CM when it was working every other way. I stumbled upon your atricle about the logging out then back in. It made my day. Thank you!

  17. 

    Really got. Love your work Chris

  18. 

    Hi CJWDEV!

    Thanks for great information and blogpost!

    I am trying to run this on a Windows 8 machine but I can’t seem to get StartConsoleSession to work.

    I have tried running it with PSEXEC as System account but I always get:

    Attempting to get console session ID…
    Console session ID = 3
    Attempting to get handle to primary access token of console session user…
    Failed to get handle to primary token, the last error reported was: An attempt was made to reference a token that does not exist

    Any ideas to what I might be doing wrong?

  19. 

    Hi, I’d like to chip in on this Fonts issue.
    When I tested a msi installation with fonts on Windows 7 with psexec and with SCCM 2012 I found that installing it interactively or not made a world of difference. When I installed the msi with psexec -s the fonts were only avaible after logging of and on again. With psexec -i -s the fonts were avaiable for the currently logged on user right after the installation. Same went for the SCCM 2012 setting ‘Allow users to interact’ on the Deployment type user experience page. This setting off –> not directly available, this setting on –> directly available.

    • 

      Yes that makes sense as the interactive switch is essentially doing the same thing that my “StartInConsoleSession” executable does – launching the process in the logged on user’s session rather than running it in the services session

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s