可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
is there a way to permanently add a Font to a Windows 7/8 PC programmatically?
I have read several posts about the AddFontResource DLL-Import, but it doesn't seem to work.
Besides of that, the MSDN Documentation says the font will be deleted after a restart of the computer, unless the font is added into the registry.
How can I install a font permanently? How can I add the font to the registry? Is it always the same name/entry?
I have to add the font dynamically on runtime, because I get the font as soon as the user selects it.
Remark: I know how to add a registry entry. My question is more about the compatibility between Windows XP, Vista, 7 and 8 and the different font-types. Maybe there is a way to start an other exe which installs the font for me.
回答1:
As you mentioned, you can launch other executables to install TrueType Fonts for you. I don't know your specific use cases but I'll run down the methods I know of and maybe one will be of use to you.
Windows has a built-in utility called fontview.exe, which you can invoke simply by calling Process.Start("Path\to\file.ttf")
on any valid TrueType Font... assuming default file associations. This is akin to launching it manually from Windows Explorer. The advantage here is it's really trivial, but it still requires user interaction per font to install. As far as I know there is no way to invoke the "Install" portion of this process as an argument, but even if there was you'd still have to elevate permissions and battle UAC.
The more intriguing option is a utility called FontReg that replaces the deprecated fontinst.exe that was included on older versions of Windows. FontReg enables you to programatically install an entire directory of Fonts by invoking the executable with the /copy switch:
var info = new ProcessStartInfo()
{
FileName = "Path\to\FontReg.exe",
Arguments = "/copy",
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden
};
Process.Start(info);
Note that the Fonts have to be in the root of wherever FontReg.exe is located. You'll also have to have administrator privileges. If you need your Font installations to be completely transparent, I would suggest launching your application with elevated permissions and approve of the UAC up front, that way when you spawn your child processes you wont need user approval Permissions stuff
回答2:
I've been having the same issue for the past few days and each solution I found was producing different problems.
I managed to come up with a working code with my colleague and I thought I'd share it for everyone. The code can be found in the following pastebin link:
Installing a font programatically in C#
EDIT
In the event this code becomes irretrievable in the future, I have copied it directly into the answer.
[DllImport("gdi32", EntryPoint = "AddFontResource")]
public static extern int AddFontResourceA(string lpFileName);
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
private static extern int AddFontResource(string lpszFilename);
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
private static extern int CreateScalableFontResource(uint fdwHidden, string
lpszFontRes, string lpszFontFile, string lpszCurrentPath);
/// <summary>
/// Installs font on the user's system and adds it to the registry so it's available on the next session
/// Your font must be included in your project with its build path set to 'Content' and its Copy property
/// set to 'Copy Always'
/// </summary>
/// <param name="contentFontName">Your font to be passed as a resource (i.e. "myfont.tff")</param>
private static void RegisterFont(string contentFontName)
{
// Creates the full path where your font will be installed
var fontDestination = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Fonts), contentFontName);
if (!File.Exists(fontDestination))
{
// Copies font to destination
System.IO.File.Copy(Path.Combine(System.IO.Directory.GetCurrentDirectory(), contentFontName), fontDestination);
// Retrieves font name
// Makes sure you reference System.Drawing
PrivateFontCollection fontCol = new PrivateFontCollection();
fontCol.AddFontFile(fontDestination);
var actualFontName = fontCol.Families[0].Name;
//Add font
AddFontResource(fontDestination);
//Add registry entry
Registry.SetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts",actualFontName, contentFontName, RegistryValueKind.String);
}
}
回答3:
According to docs of AddFontResource()
This function installs the font only for the current session. When the
system restarts, the font will not be present. To have the font
installed even after restarting the system, the font must be listed in
the registry.
So the best option i found is to copy the font to windows font directory
File.Copy("MyNewFont.ttf",
Path.Combine(Environment.GetFolderPath(SpecialFolder.Windows),
"Fonts", "MyNewFont.ttf"));
And then add respective entries in registery,Like
Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts");
key.SetValue("My Font Description", "fontname.tff");
key.Close();
回答4:
This solution is clean, works without reboot(!) but it does show a "Installing font..." dialogbox (which disappears by itself).
First, add a reference to system32\shell32.dll in your project.
And then, use just these 3 lines of code to install a font:
Shell32.Shell shell = new Shell32.Shell();
Shell32.Folder fontFolder = shell.NameSpace(0x14);
fontFolder.CopyHere(@"path_to\the_font.ttf");
3 lines of code :)
回答5:
internal static void InstalarFuente(string NombreFnt,string RutaFnt)
{
string CMD = string.Format("copy /Y \"{0}\" \"%WINDIR%\\Fonts\" ", RutaFnt);
EjecutarCMD(CMD);
System.IO.FileInfo FInfo = new System.IO.FileInfo(RutaFnt);
CMD = string.Format("reg add \"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\" /v \"{0}\" /t REG_SZ /d {1} /f", NombreFnt, FInfo.Name);
EjecutarCMD(CMD);
}
public static void EjecutarCMD(string Comando)
{
System.Diagnostics.ProcessStartInfo Info = new System.Diagnostics.ProcessStartInfo("cmd.exe");
Info.Arguments = string.Format("/c {0}", Comando);
Info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
System.Diagnostics.Process.Start(Info);
}
.....
回答6:
If you have Visual Studio 2017, you can create a new Visual Studio Installer - Setup Project. You can edit the installer to remove dialog boxes, only leaving the Finish dialog to show the user that it ran OK.
From the File System on Target Machine (in the Visual Studio project), add the special directory called Fonts. Then add all the fonts you want into the Fonts directory. If you look at the Properties of each font you add, you will see that Visual Studio has already assumed you want to register each font.
Compile the project, and you have an MSI with a setup.exe that you can deploy. Of course, you need to run it as an administrator, but other than that, this little program works fast and efficiently. I found that this was the easiest way to install fonts on Windows.