Hi Guys,

We have a simple MSI package that installs a few files to Program Files and then installs 4 font files (they are in the Font table in the MSI) and we deploy this package via SCCM, so the MSI gets installed silently from the services session. All machines that we deploy this package to are Windows 7 (mixture of 32 bit and 64 bit).

The problem is, if a user is currently logged on when the MSI is installed by the SCCM agent service, the fonts are not available to any applications in that user's session until they log out and log back in. If we run exact same silent MSI installation but as the user (making them admin temporarily) then the fonts are available straight away.

Basically I just want to know if anyone else experiences this issue and if they have a solution?


I will add that I've spent the last 3 days looking into this and have come up with my own theory as to why this works the way it does and have created a rather elaborate solution to the problem (involving writing two applications that use various Windows APIs add the fonts to the current user's session until they log out) but I just wondered how other people deal with this. I'll explain my full solution and post a link to the programs I created if anyone is interested.
0 Comments   [ + ] Show Comments

Comments

Please log in to comment

Rating comments in this legacy AppDeploy message board thread won't reorder them,
so that the conversation will remain readable.

Answers

0
Hey Chris,
You could try a coded restart of the Windows Font Cache Service after your install, fntcache.dll is the library that runs under svchost. This may initialise your new fonts without a logoff/restart and without admin rights having to be added to your standard users.

Rich
Answered 06/12/2011 by: rich0864
Orange Belt

Please log in to comment
0
Thanks for the suggestion, hadn't thought of that, but I'm afraid it doesn't work. The service was not started but if I start/restart it the fonts still do not work until logging out and back in.
Oh and making the user's admins makes no difference - I was logged on as a Domain Admin when I tested it just now.

I've refined my current solution and got it all working correctly now, so I'm happy to stick with that. I was just curious if other people had the same issue and how they got round it.
I've posted a full write up that includes an explanation of why this happens and download links for the two apps I wrote as part of the solution just in case it helps other people out: http://cjwdev.wordpress.com/2011/06/12/install-fonts-for-logged-on-user-via-sccm-package/

If anyone else wants to chip in with how they get around this issue (or if you just live with it) I'd be happy to hear your comments :)
Answered 06/13/2011 by: chris128
Orange Belt

Please log in to comment
0
Does your MSI have the RegisterFonts action? If it does, is it conditioned?
Answered 06/13/2011 by: VBScab
Red Belt

Please log in to comment
0
I can check that but I think you might be misunderstanding the issue.
The problem is not that the fonts don't get installed - it is that they are not available to the currently logged on user when the MSI is run from SCCM (or indeed any service). Once the user has logged out and logged back in then the fonts work fine.
Also, if the user runs the exact same silent MSI installation manually then the fonts work straight away without a logoff/logon.

Therefore I am pretty sure it is not an issue with the MSI itself, unless of course you can tell me that this is not normal behaviour and that you can execute an MSI installation from a service and the fonts work for the currently logged on user instantly.

Like I said, I believe I've figured out why this is and have come up with my own solution. See previous post for a link to my blog post where I have written it up in full :)
Answered 06/13/2011 by: chris128
Orange Belt

Please log in to comment
0
So, as it turns out I now have a similar problem to sort out only I do not have the msi element of the problem.

I have modified a VB Script as follows:


option explicit
Dim arrFileLines()
parseFileToArray "fonts.txt", arrFileLines
'echoArraySimple arrFileLines
echoArrayNice arrFileLines
wscript.quit

'=====

sub parseFileToArray(byVal pFilename, byRef pArray)
'open - declare variables and stuff
dim objFSO,objFile, hwmArray,x 'hwm = HighWaterMark
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(pFilename, ForReading)
CONST ForReading = 1
CONST INIT = 100 'INIT = Inital Array Size
CONST EXP = 250 'EXP = expand the array by this much if it fills up
'main - do the work
hwmArray = -1
redim pArray(INIT)
Do Until objFile.AtEndOfStream
hwmArray = hwmArray + 1
x = hwmArray
if hwmArray >= ubound(pArray) then redim preserve pArray(hwmArray + EXP) 'if the array fills up, buffer EXP more entries
pArray(x) = objFile.ReadLine
Loop
redim preserve pArray(hwmArray) 'shrink array to actual size
'close - set the return value, close objects and stuff
objFile.Close
set objFile = nothing
set objFSO = nothing
end sub

sub echoArrayNice(byRef pArray)
dim strLine,msg
dim oShell
Set oShell = WScript.CreateObject("WScript.Shell")
For Each strLine in pArray
' msg = "RegisterFont.exe add " & strLine
msg = "StartInConsoleSession.exe CurrentSessionFonts.exe add " & strLine & " c:\windows\fonts\" & strLine
' wscript.echo msg
oShell.run msg
Next
end sub


I have all of my ttf's sitting at the root of the folder I am working out of and the fonts.txt lists the font name to populate the array. I have successfully tested that the array is doing what I need by echoing the commands I am formatting to pass to the cmd line, so I know all the prelim stuff is working.

I have tried this with both of Chris' exe's as well as the 'registerfonts.exe' that I found referenced on Chris' technet social posting.

All 3 exe's run without errors when called [respectively, not running all 3 at the same time] by the script but alas no fonts, either immediately or with a logoff/logon [I am testing this with a domain admin account as the logged on user, running from cmd line as system using psexec].

Any ideas?

Cheers
Rich

EDIT:
Just ran Chris' exe's from cmd line [cmd: >StartInConsoleSession.exe CurrentSessionFonts.exe add exampleFont.ttf %windir%\fonts\exampleFont.ttf] as SYSTEM for one ttf only and received the following:

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


----------------------------------

OK, the latest output:

>StartInConsoleSession.exe CurrentSessionFonts.exe add exampleFont.ttf %windir%\fonts\exampleFont.ttf

StartInConsoleSession.exe
Version 1.0.0.0
Developed by Chris Wright (cwright@cjwdev.co.uk)
Command line = "CurrentSessionFonts.exe" "add" "exampleFont.ttf" "C:\Windows\fonts\exampleFont.ttf"

Wait for exit = False
Attempting to get console session ID...

Console session ID = 1
Attempting to get handle to primary access token of console session user...
Failed to get handle to primary token, the last error reported was: A required privilege is not held by the client

C:\run\IDAutomationFonts>CurrentSessionFonts.exe add exampleFont.ttf %windir%\fonts\exampleFont.ttf

CurrentSessionFonts.exe
Version 1.0.0.0
Developed by Chris Wright (cwright@cjwdev.co.uk)
Font successfully added (exampleFont.ttf)


Didn't run directly as SYSTEM this time, only with domain admin creds...Refresh on %windir%\fonts shows nothing and logoff/logon shows nothing as well.

I could use your help on this Chris...sorry to bother you man.
Answered 06/18/2011 by: rich0864
Orange Belt

Please log in to comment
0
No worries I'm happy to try and help :)

First of all, what OS is this running on? As I mentioned earlier, I only tested this on Windows 7.

Anyway, I think you have misunderstood how to use my currentsessionfonts.exe app - you are passing in the font file name and then C:\Windows\Fonts\font.ttf, where as you should only be passing in the path to the font file. E.g currentsessionfonts.exe add C:\Windows\Fonts\font.ttf. Of course if your font does not already exist in C:\Windows\Fonts then your script should copy it into there first before doing this (I didn't need to do that because the MSI had already actually installed the fonts). Another thing that the MSI had already done that you will need to do is to add the font to that registry location I mentioned in my blog post: HKLM\Software\Microsoft\Windows NT\CurrentVersion\Fonts, as otherwise it will not be available after the user logs off.

As for the errors with StartInConsoleSession.exe - the first one about the token not existing I am unsure about, I would have thought you would only get that if no user was currently logged on to the console session. The second one about a required privilege not being held is understandable because that was when you were not running as Local System account and only that account has the required permissions to do this.
Answered 06/19/2011 by: chris128
Orange Belt

Please log in to comment
0
OK Yeh,
I am running on windows 7.0 and yes, you're right I did not copy the fonts to 'windows\fonts' to begin with. I also didn't include the reg entries actioned within the msi. I will mimic these actions and test again. Thanks Chris.

Cheers
Rich
Answered 06/19/2011 by: rich0864
Orange Belt

Please log in to comment
0
Got it!


Wrapped it in an sfx wrapper for good measure, which extracts to c:\temp with:
c:\temp\ExampleFonts\psexec\psexec.exe -i -s -accepteula "%windir%\system32\cmd.exe" /K "c:\temp\ExampleFonts\registerfonts.cmd"
passing as the command at extraction.

Registerfonts.cmd:

regedit.exe /S "c:\temp\ExampleFonts\ExampleFonts.reg"
copy "c:\temp\ExampleFonts\*.ttf" "%windir%\fonts" /Y
cscript.exe c:\temp\ExampleFonts\registerfonts.vbs



registerfonts.vbs:
option explicit
Dim arrFileLines()
parseFileToArray "c:\temp\ExampleFonts\fonts.txt", arrFileLines
'echoArraySimple arrFileLines
echoArrayNice arrFileLines
wscript.quit

sub parseFileToArray(byVal pFilename, byRef pArray)

'open - declare variables and stuff
dim objFSO,objFile, hwmArray,x 'hwm = HighWaterMark
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(pFilename, ForReading)

CONST ForReading = 1
CONST INIT = 100 'INIT = Inital Array Size
CONST EXP = 250 'EXP = expand the array by this much if it fills up

'main - do the work
hwmArray = -1

redim pArray(INIT)
Do Until objFile.AtEndOfStream
hwmArray = hwmArray + 1
x = hwmArray
if hwmArray >= ubound(pArray) then redim preserve pArray(hwmArray + EXP) 'if the array fills up, buffer EXP more entries
pArray(x) = objFile.ReadLine
Loop
redim preserve pArray(hwmArray) 'shrink array to actual size

'close - set the return value, close objects and stuff
objFile.Close
set objFile = nothing
set objFSO = nothing
end sub

sub echoArrayNice(byRef pArray)
dim strLine,msg,oExe1,oExe2
oExe1 = "c:\temp\ExampleFonts\StartInConsoleSession.exe"
oExe2 = "c:\temp\ExampleFonts\CurrentSessionFonts.exe"
dim oShell
Set oShell = WScript.CreateObject("WScript.Shell")
For Each strLine in pArray
msg = oExe1 & " " & oExe2 & " add " & strLine & " %windir%\fonts\" & strLine
oShell.run msg
Next
end sub


This requires a logoff/logon once done which is fine 'cos that closes all 'admin cred' sessions anyways. Once logged back on the fonts appear and are usable for the app in question.

It is something of a hack job, happy to accept suggestions on cleaning it up and simplifying. I'd love to find a way to do this without having to rely on absolute pathes but environmental permissions are restrictive and I did not want to add any 'runas' to the code as that would have just complicated things even further :)

Thanks heaps for your help Chris!
Answered 06/19/2011 by: rich0864
Orange Belt

Please log in to comment
0
It shouldn't require a logoff/logon - if it does then there's no point using my apps as that was the problem they were there to solve. If you dont care about the users needing to log off and back on then you can get rid of the whole registerfonts.vbs script you have there.
Answered 06/22/2011 by: chris128
Orange Belt

Please log in to comment
0
Yeh I was wondering about that myself. Honestly, it has already been deployed to the environment so I consider it finalized now. A logoff/logon was not altogether that important as it was an urgent requirement [unplanned!!! of course] that required a resolution ASAP.

So, as a result I have not performed any further troubleshoot or test on it.

I do have to say a big thank you though, it got me out of a jam that another caused [:@]

Cheers
Answered 06/27/2011 by: rich0864
Orange Belt

Please log in to comment
Answer this question or Comment on this question for clarity