最近工作中遇到一个需求,需要统计当前系统中包含的所有字体。在网上逛了一圈后发现了EnumFontFamiliesEx这个API好像就可以实现这个功能。这里将自己对这个API的理解做一个记录,算是对这块知识的一个总结吧。

API介绍

这里主要介绍的API就是EnumFontFamiliesEx以及它的回调函数EnumFontFamExProc。从MSDN的官方文档中可以看出,EnumFontFamiliesEx可以枚举出当前系统中符合特定字符集的所有字体。

EnumFontFamiliesEx的函数的原型如下:

int EnumFontFamiliesEx(
_In_ HDC hdc,
_In_ LPLOGFONT lpLogfont,
_In_ FONTENUMPROC lpEnumFontFamExProc,
_In_ LPARAM lParam,
DWORD dwFlags
);

在上面的参数中,hdc对应了设备上下文,可以直接根据GetDC得到。LPLOGFONT代表指向字体逻辑的指针,在进行字体遍历时,可以将该参数设置为NULL。lpEnumFontFamExProc回调函数地址,也就是EnumFontFamExProc这个函数的地址。lParam作为附加的参数用与字体信息一起传递给回调函数,本文中并未用到这个附加参数,因此直接设为0,。最后dwFlags未被使用,需要强制设置0。因此,在调用该函数时,其实就是将它所遍历到的字体信息传递给回调函数EnumFontFamExProc进行处理。既然这样,如果要统计当前系统中包含的所有字体,那么这个统计的操作就可以放在回调函数EnumFontFamExProc中进行(因为每进入一次回调函数EnumFontFamExProc,说明EnumFontFamiliesEx给它传入了一种系统字体)。既然需要在EnumFontFamExProc中处理统计逻辑,因此需要了解EnumFontFamExProc这个函数的原型。MSDN中给出了其标准的原型定义:

int CALLBACK EnumFontFamExProc(
const LOGFONT *lpelfe,
const TEXTMETRIC *lpntme,
DWORD FontType,
LPARAM lParam
);

其中,lpelfe就对应了当前处理的字体信息,但是LOGFONT这个结构体中并不包含需要的字体名称信息。为了解决这个问题,需要进行一次转型操作。如MSDN中所说,

To obtain additional information about the font, you can cast the result as an ENUMLOGFONTEX or ENUMLOGFONTEXDV structure.

将LOGFONT* 转型为ENUMLOGFONTEX *。 而根据MSDN中对ENUMLOGFONTEX这个结构体的描述

typedef struct tagENUMLOGFONTEX {
LOGFONT elfLogFont;
TCHAR elfFullName[LF_FULLFACESIZE];
TCHAR elfStyle[LF_FACESIZE];
TCHAR elfScript[LF_FACESIZE];
} ENUMLOGFONTEX, *LPENUMLOGFONTEX;

可以由elfFullName这个成员得到当前字体的名称。因此,统计字体名称就可以直接从这个结构体中的elfFullName获取。到这里,实现这样一个功能来统计当前系统中所有的字体对应的步骤就变得十分清晰的了。

1. 调用EnumFontFamiliesEx依次遍历当前系统上的每一种字体

2. 在EnumFontFamiliesEx遍历到其中一种字体时,会将字体的信息传递给回调函EnumFontFamExProc。此时,回调函数负责处理传过来的字体,并将这部分信息解析到ENUMLOGFONTEX中。

3. 在EnumFontFamiliesEx函数中,添加字体统计逻辑(其实很简单,就是将elfFullName这个字符串放入一个容器中保存)

在了解了本文的实现机制后,就可以实际动手来验证这个方案了。

验证实例

首先定义一个通用类来进行Windows系统下字体统计功能,它的定义如下

/************************************************************************/
/* file : 设计单独的类来进行统计本机字体操作
* author : Huagang Li
* date : 2014-8-25 21:40:49
* tips : 调用EnumFontFamiliesExProc回调函数遍历当前系统的所有字体
* blogs : http://www.cnblogs.com/lhglihuagang/
*/
/************************************************************************/

ifndef _SYS_FONT_H

#define _SYS_FONT_H

include <windows.h>

include <string>

include <set>

//////////////////////////////////////////////////////////////////////////

// CWindowHelper 提供遍历统计Windows系统中字体的接口

class CWindowHelper {

public:

static std::set<std::wstring> sysFonts; // 用于在回调函数中保存当前遍历到的字体

static void GetSystemFonts();

static void ShowSysFonts(); private:

static enum emFindFont { STOP_FIND = 0, CONTINUE_FIND = 1 };

static int CALLBACK EnumFontFamiliesExProc( ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme, int FontType, LPARAM lParam );

}; #endif

对应的接口实现如下:

#include "SysFonts.h"
#include <tchar.h> std::set<std::wstring> CWindowHelper::sysFonts; int CALLBACK CWindowHelper::EnumFontFamiliesExProc( ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme, int FontType, LPARAM lParam )

{

if (NULL == lpelfe)

{

return emFindFont::STOP_FIND; // 返回0停止遍历

}
sysFonts.insert( lpelfe</span>-&gt;elfFullName );  <span style="color: #008000">//</span><span style="color: #008000"> 将遍历中得到的字体存入容器保存</span>
<span style="color: #0000ff">return</span> emFindFont::CONTINUE_FIND; <span style="color: #008000">//</span><span style="color: #008000"> 返回1 代表继续进行下一轮遍历</span>

}

void CWindowHelper::GetSystemFonts()

{

HDC hdc = GetDC(NULL);

LOGFONT logFont = { 0, 0, 0, 0, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, L"" };

::EnumFontFamiliesEx(hdc, &logFont, (FONTENUMPROC)EnumFontFamiliesExProc, 0, 0);

::ReleaseDC(NULL, hdc);

}

void CWindowHelper::ShowSysFonts()

{

std::wstring strRes;

std::set<std::wstring>::const_iterator iter = sysFonts.begin();

while (iter != sysFonts.end())

{

strRes = strRes + (*iter) + L" ";

++iter;

}

::MessageBox(NULL, strRes.c_str(), _T("当前系统所有字体"), MB_OK);

}

这个类的核心接口为GetSystemFonts。按照前文的实现逻辑:首先调用EnumFontFamiliesEx来遍历每一种字体,在定义响应的回调函数EnumFontFamiliesExProc来处理传入的每一种字体信息,同时在回调函数的实现体中添加字体统计逻辑。这里需要注意的是:回调函数返回值必须非0.如果返回值为0,那么遍历将会结束。因此为了能够遍历本系统中所有字体,应该返回一个非0值。

最后,测试程序的入口如下:

/************************************************************************/
/* file : 测试程序的主入口
* author : Huagang Li
* date : 2014-8-25 21:25:24
* blogs : http://www.cnblogs.com/lhglihuagang/
*/
/************************************************************************/

include <windows.h>

include "SysFonts.h"

int WINAPI WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd )

{

CWindowHelper::GetSystemFonts();

CWindowHelper::ShowSysFonts();
</span><span style="color: #0000ff">return</span><span style="color: #000000"> EXIT_SUCCESS;

}

这里,为了方便直接将所有的字体用MessageBox 显示出来了,并没有做可读性方面的优化。运行结果如下图1所示:

图1 运行后显示的所有字体

从图中可以看出,该程序统计了当前系统的字体。有一个奇怪的点是:统计的一部分字体前面带有@符号,如宋体和@宋体。至于这个@出现的原因,好像是和windows 7隐藏字体相关,具体可以参见http://stackoverflow.com/questions/11253827/too-many-fonts-when-enumerating-with-enumfontfamiliesex-function

结论

1. 通过EnumFontFamiliesEx和EnumFontFamExProc配合可以遍历系统中所有字体。

2. 上述方法得到的字体中会有重复,实现程序了为了显示单一的字体所以用Set进行了保存

3. 上述方法得到的字体部分存在@,对应了windows 7中的隐藏字体

参考链接

[1] http://msdn.microsoft.com/ZH-CN/library/windows/desktop/dd162620(v=vs.85).aspx

[2] http://msdn.microsoft.com/ZH-CN/library/windows/desktop/dd162618(v=vs.85).aspx

版权声明

    声明:未作说明,则本文为年糕原创。转载务必注明出处
    注意:转载须保留全文,如需修改请 联系作者。 

windows API 统计系统字体的更多相关文章

  1. C/C++ Windows API——获取系统指定目录(转)

    原文地址:C/C++ Windows API——获取系统指定目录 经测试,在win10 VS2017中用wprintf()输出正常,SHGetSpecialFolderPath函数也正常运行 但是用M ...

  2. 使用Windows api 获得系统时间并生成文件夹

    // 使用window api 获得系统时间 // 生成 #include "stdafx.h" #include <Windows.h> #include <d ...

  3. windows API 创建系统托盘图标

    系统托盘在我们使用的程序中很普遍,下面我们来看一个很不错的例子,使用Win32 API实现,对理解系统托盘有些帮助. [cpp] view plaincopy #include <windows ...

  4. Windows API获取系统配置文件的配置参数

    在Windows平台下获取系统配置文件(如:System.ini)的配置参数. 系统配置文件System.ini的内容如下: [SYSTEM] ServiceIP = 10.128.11.99:600 ...

  5. 调用windows api 获取系统分辨率

    c++中: int cxScreen,cyScreen; cxScreen=GetSystemMetrics(SM_CXSCREEN); cyScreen=GetSystemMetrics(SM_CY ...

  6. windows API 开发飞机订票系统 图形化界面 (二)

    首先,用到的数据结构的定义.以及全局变量和函数的声明如下: // Flight.c : 定义应用程序的入口点. // #include "stdafx.h" //订单 typede ...

  7. Windows系统字体与文件对照表

    源:Windows系统字体与文件对照表 宋体 (TrueType) = SIMSUN.TTF 黑体 (TrueType) = simhei.ttf 楷体_GB2312 (TrueType) = sim ...

  8. windows API 开发飞机订票系统 图形化界面 (一)

    去年数据结构课程设计的作品,c语言实现,图形化界面使用windows API实现. 首发在我csdn博客:http://blog.csdn.net/u013805360/article/details ...

  9. Windows API 函数列表 附帮助手册

    所有Windows API函数列表,为了方便查询,也为了大家查找,所以整理一下贡献出来了. 帮助手册:700多个Windows API的函数手册 免费下载 API之网络函数 API之消息函数 API之 ...

随机推荐

  1. A configuration error occurred during startup. Please verify the preference field with the prompt: Cannot connect to vm

    1.报错图 解决方法: Window->Preferences->MyEclipse Enterprice Workbench->Servers->Tomcat->选择你 ...

  2. 用delphi的THTTPRIO控件调用了c#写的webservice。

    用delphi的THTTPRIO控件调用了c#写的webservice. 下面是我调试时遇到的一些问题: 1,导入wsdl文件:file--new----other----wenservice---W ...

  3. [Exchange]2个不同域之间互发邮件

    1.互相添加DNS. 不多说,构建信任域也需要这样的 2.DNS中添加Exchange的地址解析. 3.配置Exchange. 在exchange里面添加对方的exchange信息 在Receiver ...

  4. HDOJ 1237题 简单计算器

    简单计算器 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submiss ...

  5. LOL游戏程序中对一些函数的Hook记录(Win10 x64)

    [PC Hunter Standard][League of Legends.exe-->Ring3 Hook]: 108Hooked Object Hook Address and Locat ...

  6. lightoj 1033 区间dp

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1033 #include <cstdio> #include <cst ...

  7. xCode里面设置NSZombieEnabled定位错误

    打开product ->scheme->Edit scheme

  8. tableview: 实现tableview 的 section header 跟随tableview滑动

    方法一:(只有一个headerView)一段 如果你的tableview恰好只有一个headerView,实现这种效果就好办了.把要设置的headerView设置成tableView的header而不 ...

  9. webview 本地上传文件

    参考http://blog.csdn.net/zhtsuc/article/details/49154099 直接上代码   public class MainActivity1 extends Ac ...

  10. android 42 获取图片

    资源中获取图片:可以从工程assets文件夹.res/drawble文件夹.sd卡.服务端下载图片. 页面: <LinearLayout xmlns:android="http://s ...