最近工作中遇到一个需求,需要统计当前系统中包含的所有字体。在网上逛了一圈后发现了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. 【转】ByteBuffer 到底怎么用?网络编程中一点总结!--不错

    原文网址:http://cuisuqiang.iteye.com/blog/1443212 做tcp网络编程,要解析一批批的数据,可是数据是通过Socket连接的InputStream一次次读取的,读 ...

  2. Java中的包

    包:定义包用package关键字. 1:对类文件进行分类管理. 2:给类文件提供多层名称空间. 如果生成的包不在当前目录下,需要最好执行classpath,将包所在父目录定义到classpath变量中 ...

  3. 【算法入门】广度/宽度优先搜索(BFS)

    广度/宽度优先搜索(BFS) [算法入门] 1.前言 广度优先搜索(也称宽度优先搜索,缩写BFS,以下采用广度来描述)是连通图的一种遍历策略.因为它的思想是从一个顶点V0开始,辐射状地优先遍历其周围较 ...

  4. HDU 4720 Naive and Silly Muggles 2013年四川省赛题

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4720 题目大意:给你四个点,用前三个点绘制一个最小的圆,而这三个点必须在圆上或者在圆内,判断最一个点如 ...

  5. 局域网内通过UDP协议进行传输接受数据——AsyncUdpSocket

    在相同的局域网内,可以通过Udp协议进行数据的传输和接收,Udp协议与Http协议不同,Udp更加方便快捷,省去了很多步骤,但是也有很多传输问题,在局域网内小范围传输数据时Udp还是非常能够胜任的. ...

  6. Hadoop Hive概念学习系列之什么是Hive?(一)

    参考  <Hadoop大数据分析与挖掘实战>的在线电子书阅读                   http://yuedu.baidu.com/ebook/d128cf8e33687e21 ...

  7. TOMCAT启动完成但是ECLIPSE仍然显示starting....

    最近重新部署了一个TOMCAT服务,但是启动碰到个问题,虽然TOMCAT控制台已显示启动成功,但是ECLIPSE右下角仍然一直显示STARTING,最后TOMCAT超时,启动失败. 之前以为是拷贝工程 ...

  8. python处理xml的常用包(lib.xml、ElementTree、lxml)

    python处理xml的三种常见机制 dom(随机访问机制) sax(Simple APIs for XML,事件驱动机制) etree python处理xml的三种包 标准库中的xml Fredri ...

  9. SQL 按月统计(两种方式) 分类: SQL Server 2014-08-04 15:36 154人阅读 评论(0) 收藏

    (1)Convert 函数 select Convert ( VARCHAR(7),ComeDate,120) as Date ,Count(In_code) as 单数,Sum(SumTrueNum ...

  10. android edittext 点击回车会响应两次的解决方案

    由于Key有Down和Up事件,所以会执行两次. class editTextOnKeyClickListener implements etOnKeyClickListener { @Overrid ...