【问题标题】:How to install a Font programmatically (C#)如何以编程方式安装字体(C#)
【发布时间】:2014-02-24 11:43:03
【问题描述】:

有没有办法以编程方式将字体永久添加到 Windows 7/8 PC? 我已经阅读了几篇关于 AddFontResource DLL-Import 的帖子,但它似乎不起作用。

除此之外,MSDN Documentation 表示该字体将在重新启动计算机后被删除,除非该字体被添加到注册表中。

如何永久安装字体?如何将字体添加到注册表?它总是相同的名称/条目吗?

我必须在运行时动态添加字体,因为我会在用户选择字体后立即获得该字体。

备注:我知道如何添加注册表项。我的问题更多是关于 Windows XP、Vista、7 和 8 以及不同字体类型之间的兼容性。也许有一种方法可以启动另一个为我安装字体的 exe。

【问题讨论】:

  • 正如我所指出的,我阅读了其中几篇文章,但他们没有回答我的问题。我希望永久安装字体,并且还询问了有关注册表的信息。

标签: c# .net winforms fonts


【解决方案1】:

正如您所提到的,您可以启动其他可执行文件来为您安装 TrueType 字体。我不知道你的具体用例,但我会介绍我知道的方法,也许其中一种对你有用。

Windows 有一个名为 fontview.exe 的内置实用程序,您可以通过在任何有效的 TrueType 字体上调用Process.Start("Path\to\file.ttf") 来调用它...假设默认文件关联。这类似于从 Windows 资源管理器手动启动它。这里的优点是它真的很微不足道,但它仍然需要每个字体的用户交互才能安装。据我所知,没有办法将此过程的“安装”部分作为参数调用,但即使有,您仍然必须提升权限并与 UAC 作斗争。

更有趣的选项是一个名为FontReg 的实用程序,它替换了旧版本 Windows 中包含的已弃用的 fontinst.exe。 FontReg 使您能够通过使用 /copy 开关调用可执行文件以编程方式安装整个字体目录:

    var info = new ProcessStartInfo()
        {
            FileName = "Path\to\FontReg.exe",
            Arguments = "/copy",
            UseShellExecute = false,
            WindowStyle = ProcessWindowStyle.Hidden

        };

   Process.Start(info);

请注意,字体必须位于 FontReg.exe 所在位置的根目录中。您还必须拥有管理员权限。如果您需要完全透明的字体安装,我建议您使用提升的权限启动您的应用程序并预​​先批准 UAC,这样当您生成子进程时,您不需要用户批准 Permissions stuff

【讨论】:

  • 是的,我已经以提升的权限启动,所以应该没问题。我会告诉自己你写下的两种方法。非常感谢!
  • 好的,我决定用 FontReg.exe 来做。看起来这是安装字体的最佳方式。感谢您的支持!
  • @ElMac 没问题。我相信源与其他可分发产品捆绑在一起,因此如果您愿意,您可以窥视并查看它是如何完成的。我只是略过逻辑,但它看起来在同一逻辑路径上:复制然后注册。
  • 我也发现了那个工具并打算使用它。一个简短的说明,该工具附带的自述文件特别指出它不是包含 FontReg.exe 的文件夹,而是 CURRENT 文件夹 - 这可能不一样。例如,您在命令提示符下,在文件夹 c:\fonts 中,运行 c:\myTools\fontreg.exe,它将在 c:\fonts 中查找字体,而不是在 c:\myTools 中查找字体。跨度>
【解决方案2】:

过去几天我一直遇到同样的问题,我找到的每个解决方案都会产生不同的问题。

我设法和我的同事一起想出了一个有效的代码,我想我会分享给大家。代码可以在以下 pastebin 链接中找到:

Installing a font programatically in C#

编辑 如果将来此代码无法恢复,我已将其直接复制到答案中。

[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);
    }
}

【讨论】:

  • 必要的namespaces:using Microsoft.Win32; using System.Drawing.Text; using System.IO; using System.Runtime.InteropServices;
【解决方案3】:

根据AddFontResource()的文档

这个函数只为当前会话安装字体。当。。。的时候 系统重新启动,字体将不存在。拥有字体 即使在重新启动系统后安装,字体必须列在 注册表。

所以我发现最好的选择是将字体复制到 windows 字体目录

File.Copy("MyNewFont.ttf",
    Path.Combine(Environment.GetFolderPath(SpecialFolder.Windows),
        "Fonts", "MyNewFont.ttf"));

然后在注册表中添加相应的条目,点赞

Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts");
                    key.SetValue("My Font Description", "fontname.tff");
                    key.Close();

【讨论】:

  • ex = {"访问路径 'C:\Windows\Fonts\fontname.otf' 被拒绝。"}。在 windows 10 下测试
  • 编辑,抱歉以为我是以管理员身份运行的。如果管理员似乎可以工作,但字体实际上并没有安装
  • 你发现了吗?
【解决方案4】:

建立在 Erik Bongers 答案的基础上,该答案具有无需重新启动即可工作并显示“正在安装字体...”对话框,该对话框自行消失,但似乎不适用于 Windows 8+

using System;
using System.IO;

namespace YourApp.Common
{
    public static class FontHandling
    {
        public static bool IsFontInstalled(string fontName)
        {
            using (var testFont = new System.Drawing.Font(fontName, 8))
                return 0 == string.Compare(fontName, testFont.Name, StringComparison.InvariantCultureIgnoreCase);
        }

        public static void InstallFont(string fontSourcePath)
        {
            var shellAppType = Type.GetTypeFromProgID("Shell.Application");
            var shell = Activator.CreateInstance(shellAppType);
            var fontFolder = (Shell32.Folder)shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { Environment.GetFolderPath(Environment.SpecialFolder.Fonts) });
            if (File.Exists(fontSourcePath))
                fontFolder.CopyHere(fontSourcePath);
        }
    }
}

不应直接从 system32 文件夹中引用所需的 shell32.dll。而是转到“AddReferences --> COM”并在那里搜索“Microsoft Shell Controls And Automation”并引用它。

最后,如果您想确保您的程序存在自定义字体,您可以将字体添加到源代码中(例如,在单独的 Fonts 文件夹中)并设置其属性:构建操作:无,复制到输出目录:如果较新则复制。

【讨论】:

  • 很高兴看到这是一个活生生的问题,人们正在不断更新答案?
【解决方案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】:

这个解决方案很干净,无需重启(!),但它确实显示了一个“正在安装字体...”对话框(它会自行消失)。

首先,在您的项目中添加对 system32\shell32.dll 的引用。
然后,只需使用这 3 行代码来安装字体:

Shell32.Shell shell = new Shell32.Shell();
Shell32.Folder fontFolder = shell.NameSpace(0x14);
fontFolder.CopyHere(@"path_to\the_font.ttf");

3行代码:)

【讨论】:

  • path_to 是什么意思?
【解决方案7】:

如果您有 Visual Studio 2017,则可以创建一个新的 Visual Studio 安装程序 - 安装项目。您可以编辑安装程序以删除对话框,只留下“完成”对话框以向用户显示它运行正常。

从目标机器上的文件系统(在 Visual Studio 项目中)添加名为 Fonts 的特殊目录。然后将所需的所有字体添加到 Fonts 目录中。如果您查看您添加的每种字体的属性,您会发现 Visual Studio 已经假定您要注册每种字体。

编译项目,您将拥有一个带有 setup.exe 的 MSI,您可以部署它。当然,您需要以管理员身份运行它,但除此之外,这个小程序可以快速有效地运行。我发现这是在 Windows 上安装字体的最简单方法。

【讨论】:

  • 我将其作为现有客户的独立项目进行 - 我们需要一种快速获取字体的方法。对于新客户,我将此技术添加到其安装程序项目中(我已经为该应用程序创建了一个 VS Setup 项目),因此效果很好。两次。
【解决方案8】:

在名为 InstallFontsHelpers 的项目中创建 2 个新文件夹。

在文件夹Helpers中,新建一个类文件Fonts.cs并将以下代码复制进去:

using System.Runtime.InteropServices;

namespace myFirstAzureWebApp.Helpers
{
    public class Fonts
    {
        [DllImport("gdi32", EntryPoint = "AddFontResource")]
        public static extern int AddFontResourceA(string lpFileName);
        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern int AddFontResource(string lpszFilename);
    }
}

将所需字体从本地字体文件夹 (C:\Windows\Fonts) 复制到 InstallFonts 文件夹。为了进行测试,只需将少数字体复制到安全空间。将属性 Build Action 设置为 Content 并 Copy to Output Directory 以始终复制

在项目的根文件夹中找到文件 Global.asax 并将其替换为以下代码:

using myFirstAzureWebApp.Helpers;
using System.IO;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace myTextControlAzureApp
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            foreach (string font in Directory.GetFiles(
                Path.Combine(HttpRuntime.AppDomainAppPath, "InstallFonts")))
            {
                Fonts.AddFontResource(font);
            }

            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-28
    • 1970-01-01
    • 2010-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多