我有下面的一个函数,用于通过将字体 (.ttf) 复制到 Windows 字体文件夹中并触发 WM_FONTCHANGE 消息来安装字体。然而,在 Windows Explorer 中,该字体不会立即显示出来。
运行后,当我通过控制面板打开字体时,我的字体不会显示在那里。当我打开 C:\Windows\Fonts\ 时,它也不会显示在那里。
但是,我可以确认我的 .ttf 文件确实存在。在命令提示符中导航到此处,我可以看到我的字体文件。当我打开字符映射工具时,我的字体出现在列表中。并且该字体可在我的应用程序中使用。我必须重新启动 explorer.exe 才能在 Windows Explorer 视图中显示它。我甚至尝试以管理员身份 (提升) 运行我的应用程序,但还是没有成功。
我认为 WM_FONTCHANGE 消息应该能解决这个问题,但显然这并没有起作用。
在进行字体安装时,我缺少了什么步骤,以确保 Windows 能够识别它?
然而,这也存在问题。返回值为
运行后,当我通过控制面板打开字体时,我的字体不会显示在那里。当我打开 C:\Windows\Fonts\ 时,它也不会显示在那里。
但是,我可以确认我的 .ttf 文件确实存在。在命令提示符中导航到此处,我可以看到我的字体文件。当我打开字符映射工具时,我的字体出现在列表中。并且该字体可在我的应用程序中使用。我必须重新启动 explorer.exe 才能在 Windows Explorer 视图中显示它。我甚至尝试以管理员身份 (提升) 运行我的应用程序,但还是没有成功。
我认为 WM_FONTCHANGE 消息应该能解决这个问题,但显然这并没有起作用。
在进行字体安装时,我缺少了什么步骤,以确保 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;
使用方法:
Result:= InstallFont(Application.Handle, 'C:\MyTestFont.ttf');
更新
在下面的回答评论中建议通过shell而不是Windows API安装字体。因此,我编写了这个函数来实现基本相同的功能:
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;
然而,这也存在问题。返回值为
31
(表示出现错误),当我调用GetLastError
时,它告诉我1155
("没有与此操作指定的文件相关联的应用程序。") 我还尝试了下面答案中的特定解决方法,但没有成功。我既使用了AddFontResource
并编写了适当的注册表键,同时尝试了安装/卸载/重新尝试该字体安装的组合。
ShellExecute
。它的错误处理是无用的。你为什么要调用GetLastError
?文档中哪里说要这样做了呢?ShellExecute
只存在于与旧程序兼容的情况下。你应该调用ShellExecuteEx
。不管怎样,你需要提升权限才能执行此操作吗? - David HeffernanShellExecute
可以执行在文件类上定义的动作。但是在这种情况下会失败,因为真正类型字体(ttffile
)的文件类没有直接定义动作。它实现了IContextMenu
并将其他项注入到右键菜单中,其中之一是动作“install”,但只能通过IContextMenu::InvokeCommand
调用。ShellExecute
不知道如何执行此操作。然而,这可能并不是您想要的,因为无法关闭用户交互,包括提示是否删除字体等。 - Jonathan Gilbert