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. However, that font does not immediately become visible across Windows Explorer.
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.
However I can confirm that my .ttf file is really there. 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. 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.
What am I missing in this Font Installation to make sure Windows is aware of it?
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. 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.")
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.
ShellExecute. Its error handling is useless. Why did you callGetLastError? Where does it say to do that in the documentation.ShellExecuteonly exists for compat with old programs. You are expected to callShellExecuteEx. Anyway, don't you need to be elevated to do this?ShellExecutecan execute verbs defined on file classes. It fails in this case because the file class for true-type fonts (ttffile) doesn't define verbs directly on itself. It does implementIContextMenuand inject additional items into the right-click menu, and one of those items is verb"install", but that can only be invoked viaIContextMenu::InvokeCommand.ShellExecutedoesn't know how to do this. This probably isn't what you want anyway, though, as there is no way to shut off the user interaction, including prompting to delete the font if it already exists etc.