简体   繁体   English

安装字体并使 Windows 感知

[英]Installing font and making Windows aware

I have a function below which installs a font ( .ttf ) into Windows by copying it into the Windows font folder and then triggering the WM_FONTCHANGE message.我在下面有一个函数,它通过将字体 ( .ttf ) 复制到 Windows 字体文件夹然后触发WM_FONTCHANGE消息来将字体 ( .ttf ) 安装到 Windows 中。 However, that font does not immediately become visible across Windows Explorer.但是,该字体不会立即在 Windows 资源管理器中可见。

After running this, when I open Fonts through the Control Panel, my font does not show there.运行后,当我通过控制面板打开字体时,我的字体没有显示在那里。 And when I open C:\\Windows\\Fonts\\ it does not show there either.当我打开C:\\Windows\\Fonts\\它也不显示在那里。

However I can confirm that my .ttf file is really there.但是我可以确认我的.ttf文件确实存在。 Navigating here with the Command Prompt, I can see my font file.使用命令提示符在此处导航,我可以看到我的字体文件。 When I open the Character Map utility, my font is listed here.当我打开字符映射实用程序时,此处列出了我的字体。 And the font is usable in my application.该字体可用于我的应用程序。 I have to restart explorer.exe to get it to show within the Windows Explorer views.我必须重新启动explorer.exe才能让它显示在 Windows 资源管理器视图中。 I've even tried running my app as administrator (elevated), and still no luck.我什至尝试以管理员身份运行我的应用程序(提升),但仍然没有运气。

I thought the WM_FONTCHANGE message was supposed to take care of this but apparently this is not doing the trick.我认为WM_FONTCHANGE消息应该解决这个问题,但显然这并没有WM_FONTCHANGE

What am I missing in this Font Installation to make sure Windows is aware of it?我在此字体安装中缺少什么以确保 Windows 知道它?

uses
  SysUtils, ShlObj, ComObj, ActiveX;

function SystemDir(Handle: THandle; Folder: Integer): String;
var
  R: HRESULT;
  PIDL: PItemIDList;
  Path: array[0..MAX_PATH] of Char;
begin
  Result:= '';
  R:= SHGetSpecialFolderLocation(Handle, Folder, PIDL);
  if R = S_OK then begin
    if SHGetPathFromIDList(PIDL, Path) then
      Result:= StrPas(Path);
  end;
end;

function InstallFont(Handle: THandle; const Filename: String): Boolean;
var
  Dir, FN: String;
begin
  Result:= False;
  FN:= ExtractFileName(Filename);
  Dir:= IncludeTrailingPathDelimiter(SystemDir(Handle, CSIDL_FONTS));
  Result:= FileExists(Filename);
  if Result then begin
    Result:= CopyFile(PChar(Filename), PChar(Dir + FN), False);
  end;
  SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
end;

Usage:用法:

Result:= InstallFont(Application.Handle, 'C:\MyTestFont.ttf');

UPDATE更新

It was suggested in the comments of an answer below to install the font via the shell instead of Windows API.在下面的答案的评论中建议通过外壳而不是 Windows API 安装字体。 So, I wrote this function to essentially accomplish the same:所以,我写了这个函数来基本上完成相同的任务:

function InstallFont2(Handle: THandle; const Filename: String): Boolean;
var
  R: HINST;
begin
  Result:= False;
  R:= ShellExecuteW(Handle, 'install', PWideChar(Filename), nil, nil, SW_HIDE);
  Result:= R > 32;
end;

However this too is problematic.然而这也是有问题的。 The return value is 31 (indicating an error) and when I call GetLastError it tells me 1155 ("No application is associated with the specified file for this operation.")返回值是31 (表示错误),当我调用GetLastError它告诉我1155 (“没有应用程序与此操作的指定文件相关联。”)

I also tried the particular resolution in the answer below, but to no avail.我也在下面的答案中尝试了特定的分辨率,但无济于事。 I both used AddFontResource and written the appropriate registry key - while trying combinations of uninstalling/restarting/retrying with this font installation.我都使用了AddFontResource并编写了适当的注册表项 - 同时尝试卸载/重新启动/重试与此字体安装的组合。

WM_FONTCHANGE only notifies applications of a new font in the system, but it doesn't actually tell the system what the new font is. WM_FONTCHANGE只通知应用程序系统中的新字体,但它实际上并没有告诉系统新字体是什么。

Before sending WM_FONTCHANGE you need to call AddFontResource to add the font to the system font table.在发送WM_FONTCHANGE之前,您需要调用AddFontResource将字体添加到系统字体表中。 If you want the font to remain after a reboot, you also need to add an entry to the registry key HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts (see the documentation for AddFontResource for more information).如果您希望字体在重新启动后保留,您还需要向注册表项HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts添加一个条目(有关详细信息,请参阅AddFontResource的文档)。

I have just traced through exactly what Windows 7 does when it installs a font.我刚刚跟踪了 Windows 7 在安装字体时所做的事情。 Here is a summary:这是一个总结:

  • If the font is a true-type font and its name doesn't already end with " (TrueType)" then it appends this.如果字体是 true-type 字体并且其名称尚未以" (TrueType)"结尾,则它会附加它。
  • If the font already exists, it can uninstall it in order to reinstall it:如果字体已经存在,它可以卸载它以重新安装它:
    • It calls RemoveFontResourceW.它调用 RemoveFontResourceW。
    • The registry entry describing the font, if any, is deleted out of SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts .描述字体的注册表项(如果有)从SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts删除。
  • It takes the filename you are trying to install, and if that already exists in the Fonts directory, then it scans for a unique filename by repeatedly adding 1 to a counter and then formatting "basename_X.ttf" where X is in hexadecimal.它采用您尝试安装的文件名,如果该文件名已存在于 Fonts 目录中,则它会通过向计数器重复加 1 并格式化"basename_X.ttf"来扫描唯一的文件名,其中 X 为十六进制。 So eg if "myfont_1.ttf" through "myfont_9.ttf" already exist, then it will try "myfont_A.ttf" next.因此,例如,如果"myfont_1.ttf""myfont_9.ttf"已经存在,那么接下来它将尝试"myfont_A.ttf"
  • It copies the file you supplied to this free filename it has identified.它将您提供的文件复制到它已识别的这个免费文件名。
  • It calls AddFontResourceW on the target path.它在目标路径上调用AddFontResourceW
  • It writes an entry to SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts based on the "(TrueType)"-qualified name of your font whose value is the target filename without path.它根据SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts的“(TrueType)”限定名称向SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts写入一个条目,该名称的值为不带路径的目标文件名。
  • It does a thing I couldn't quite figure out, creating a PropertyStore and putting a bunch of values into it.它做了一件我不太明白的事情,创建了一个 PropertyStore 并将一堆值放入其中。 I'm not sure what exactly it does with the resulting property store, but it calls it a FID .我不确定它对生成的属性存储究竟做了什么,但它称之为FID
  • It waits for 2 seconds by calling Sleep .它通过调用Sleep等待 2 秒。
  • It calls PostMessageW(HWND_BROADCAST, WM_SETTINGSCHANGE, NULL, L"fonts")它调用PostMessageW(HWND_BROADCAST, WM_SETTINGSCHANGE, NULL, L"fonts")
  • It calls PostMessageW(HWND_BROADCAST, WM_FONTCHANGE, NULL, NULL)它调用PostMessageW(HWND_BROADCAST, WM_FONTCHANGE, NULL, NULL)
  • It calls SHGetSpecialFolderLocation(CSIDL_FONTS) and then passes the resulting IDLIST into SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, idlist, NULL) .它调用SHGetSpecialFolderLocation(CSIDL_FONTS)然后将结果IDLIST传递到SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, idlist, NULL)

I suspect it is these latter three that are crucial in getting the system to recognize the new font in other applications and in the Fonts folder.我怀疑后三个对于让系统识别其他应用程序和 Fonts 文件夹中的新字体至关重要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM