Monday, 28 June 2010

ClickOnce does not install fonts - Install fonts using C#

ClickOnce does not install fonts. ClickOnce is designed to be a non-impactful install, and installing fonts is considered impactful to a user's computer. ClickOnce is only a deployment technology, it does not offer any support for font installation, so this operation requires extra coding. We have to code the font installation logic in the application. To install font on client machines, we have to create code just like the following.

ClickOnce deloyment normally does not touch the system directories. This is an important difference with MSI deployment technology. ClickOnce deployment does not cover:
· "Install to GAC"(GAC is another system directory)
· "Write to Registry"
· "Install for All Users" etc... While these can all be done with MSI:

Take a look to "Key Differences" between ClickOnce and MSI:
http://msdn.microsoft.com/en-us/library/142dbbz4(VS.90).aspx

So we have to code ourselves for the font installation, first: write file from embedded resource, second: add font to windows resources, third: add registry entry so the font is also available next session, finally: solve GDI+ bug, where GDI+ must be notified with this new added font, so, a scalable font resource file needs to be created, this scalable font resource file stores the name of a TrueType font file so that GDI knows where to find the file. To create a scalable font resource file, call the GDI function CreateScalableFontResource with an integer flag, the name of the font resource file to be generated, an existing TrueType font file name, and the path to the files if they do not contain a complete path.

First: Special thanks for following threads:

http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework/topic52983.aspx
http://www.daniweb.com/forums/thread231795.html
http://www.dotnetmonster.com/Uwe/Forum.aspx/winform/18215/ClickOnce-deployment-include-font

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Drawing.Text;
using System.Windows.Forms;
namespace myNameSpace

{
class InstallFonts
{
// PInvoke to look up fonts path
[DllImport("shfolder.dll", CharSet = CharSet.Auto)]
private static extern int SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, int dwFlags, StringBuilder lpszPath);

private const int CSIDL_FONTS = 0x0014;
private const int MAX_PATH = 260;
private static string GetFontsPath()
{
StringBuilder sb = new StringBuilder(MAX_PATH);
SHGetFolderPath(IntPtr.Zero, CSIDL_FONTS, IntPtr.Zero, 0, sb);
return sb.ToString();
}

// PInvoke to 'register' fonts and broadcast addition

[DllImport("gdi32.dll")]
private static extern int AddFontResource(string lpszFilename);

[DllImport("gdi32", EntryPoint = "RemoveFontResource")]
private static extern bool RemoveFontResourceW(string lpFileName);

[DllImport("gdi32.dll")]
private static extern int CreateScalableFontResource(uint fdwHidden, string lpszFontRes, string lpszFontFile, string lpszCurrentPath);

private static IntPtr HWND_BROADCAST = new IntPtr(0xffff);
private const uint WM_FONTCHANGE = 0x001D;

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

public static void InstallFont()
{

string fontsPath = GetFontsPath();
string ttfFile9 = System.IO.Path.Combine(fontsPath, "V100009_.TTF"); ;
string fotFile9 = System.IO.Path.Combine(fontsPath, "V100009_.FOT"); ;

int ret;
if (!System.IO.File.Exists(ttfFile9))
{
//Write file from embedded resource
System.IO.File.WriteAllBytes(ttfFile9, MyFonts.V100009_);
//Allow GDI+ to be notified and determines the new fonts
//to install a TrueType font, a scalable font resource file needs to be
//created. This scalable font resource file stores the name of a TrueType
//font file so that GDI knows where to find the file. To create a scalable
//font resource file, call the GDI function CreateScalableFontResource with
//an integer flag, the name of the font resource file to be generated, an
//existing TrueType font file name, and the path to the files if they do not
//contain a complete path.
ret = CreateScalableFontResource(0, fotFile9, ttfFile9, String.Empty);
//Add font resource
ret = AddFontResource(fotFile9);
//Add registry entry so the font is also available next session
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", "C39HrP36DlTt (TrueType)", "V100009_.TTF", RegistryValueKind.String);

//Broadcast to let all top-level windows know about change
ret = SendMessage(HWND_BROADCAST, WM_FONTCHANGE, new IntPtr(0), new IntPtr(0));
//Work around to use font after installing without restarting the installing application
//It is GDI+ bug not .NET bug,
PrivateFontCollection oPFC9 = new PrivateFontCollection();
oPFC9.AddFontFile(ttfFile9);
}
}
}
}

Tuesday, 22 June 2010

An error occurred creating the configuration section handler for system.serviceModel/bindings

Suddenly I got a problem when trying to access a web service,
the problem only occurred on my PC that has "Vista SP1 x64"
operating system, my workmates hasn't this problem at all, this is the exception
string appeared:


System.TypeInitializationException: The type initializer for
'my internal class name' threw an exception. --->
System.Configuration.ConfigurationErrorsException: An error occurred creating
the configuration section handler for system.serviceModel/bindings: Could not
load type
'System.Security.Authentication.ExtendedProtection.Configuration.ExtendedProtectionPolicyElement'
from assembly 'System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
(D:\Projects\Hajj1431\Sources\Desktop\Hajj\bin\Release\Hajj.exe.Config line 38)
---> System.TypeLoadException: Could not load type
'System.Security.Authentication.ExtendedProtection.Configuration.ExtendedProtectionPolicyElement'
from assembly 'System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.


I googled and found some thing similar

here
,

here
,

here
and

here
. All of them points to a corrupted windows update with code KB982168, I
spend long time searching about KB982168 or any thing similar but nothing. Where
all of the previous threads were talking about Win2003 and WinXP updates.


I tried to restore my operating system to old date but Windows
Restore was disabled by group policy created by Domain Admin.


I tried to go deep in the problem to find which assembly cause
the problem but it seems it is some thing larger than a single assembly.


After long time of try and error I reached a solution by
installing Vista Service Pack 2.


Where, I noticed that Microsoft announced it will not support
Vista without SP2, so, I realized that any update my not work properly without
SP2, again searching, I found x64 version of mentioned SP2, it takes an hour to
download and another hour or more to install, but finally web-services is back
to work again.


Thanks god ...


Service Pack 2 (KB948465):



It is 745MB size and can be downloaded from this link:


http://www.microsoft.com/downloads/details.aspx?FamilyID=8ad69826-03d4-488c-8f26-074800c55bc3&displaylang=en&displaylang=en