您的位置:首页 > 编程语言 > C语言/C++

在C/C++中获取可执行文件的图标和信息

2013-08-14 20:35 120 查看



在写AutorunLoadViewer的过程中,需要能够获取可执行文件的图标和一些特定的文件信息(比如公司名还有文件版本.etc)

但是似乎MFC并没有提供现成的类库,于是只能自己通过API实现相应的功能



获取文件图标

关于如何获取文件图标,你可以很容易的在MSDN找到一个API——ExtracIcon

但是如果你仔细阅读文档说明,就会发现这个API并不符合我们的要求。因为ExtracIcon是从可执行文件的RC资源中找存在的图标,而不是返回在Win中显示的图标

所以,我们应该使用的API是SHGetFileInfo(关于这个API的详情,请自己查询相关文档)

每个文件都有一个SHFILEINFO结构,其中就包含了我们需要的图标。而我们要做的,就是利用SHGetFileInfo填充这个结构,然后获取图标资源即可

01
/*
02
输
入: lpszExePath(LPCTSTR) - [in]文件路径
03
输
出: HCION - Handle:成功, NULL:非正确文件类型或不存在
04
功
能: 获取可执行文件图标
05
*/
06
HICON
CQueryExeInfo::QueryExeIcon(
LPCTSTR
lpszExePath)
07
{
08
HICON
hIcon
= NULL;
09
SHFILEINFO
FileInfo;
10
11
DWORD_PTR
dwRet
= ::SHGetFileInfo(lpszExePath, 0, &FileInfo,
sizeof
(SHFILEINFO),
SHGFI_ICON);
12
13
//
目标文件不存在
14
if
(dwRet)
15
{
16
hIcon
= FileInfo.hIcon;
17
}
18
19
return
hIcon;
20
}
这里需要注意的是,我们获取的图标是系统资源,不需要时我们应该手动对其进行清理。

由于我把这些函数封装在了CQueryExeInfo中,所以清理图标的函数是static成员变量,好处是独立于实际类对象

01
/*
02
输
入: hIcon(HICON) - 图标句柄
03
输
出: -
04
功
能: 释放图标资源
05
*/
06
void
CQueryExeInfo::FreeIconBuffer(
HICON
hIcon)
07
{
08
//
此函数为static,可以不依赖类对象使用
09
::DestroyIcon(hIcon);
10
}
获取文件信息

在Win中,至少在目前为止,获取可执行文件的信息一直是一件很蛋疼的事情,以为我们要用到几个很“暧昧”的函数

和前面的SHFILEINFO类似,可执行文件的公司名、版本号之类的东西也保存在一个信息结构中,所以我们还是需要获取这个结构

CQueryExeInfo对应的成员函数是GetExeVersionInfo(protected)

01
/*
02
输
入: lpszExePath(LPCTSTR) - [in]可执行文件路径
03
输
出: DWORD - Size:VersionInfo的字节数, FALSE:获取版本信息失败
04
功
能: 加载目标文件的VersionInfo
05
*/
06
DWORD
CQueryExeInfo::GetExeVersionInfo(
LPCTSTR
lpszExePath)
07
{
08
DWORD
dwInfoSize;
09
BOOL
bRet
= FALSE;
10
11
dwInfoSize
= ::GetFileVersionInfoSize(lpszExePath, 0);
12
13
if
(!dwInfoSize)
14
{
15
return
FALSE;
16
}
17
18
//
申请内存,用于保存VersionInfo
19
m_lpVersionBuffer
=
new
TCHAR
[dwInfoSize];
20
ASSERT(m_lpVersionBuffer
!= NULL);
21
bRet
= ::GetFileVersionInfo(lpszExePath, NULL, dwInfoSize, m_lpVersionBuffer);
22
23
if
(bRet)
24
{
25
//
下面还有用
26
return
dwInfoSize;
27
}
28
29
return
FALSE;
30
}
其中m_lpVersionBuff是成员变量,类型是LPVOID,因为数据类型无法确定。这个变量保存了我们获得的文件版本信息结构

有了这个结构信息,我们就可以从里面“抽出”我们想要的东西。但是在做之前,我们还需要做一件事情——设置语言代码页(language codepages)

我们需要的东西保存在一个叫String Struct的东西里,而这个东西依赖lang-codepage。

呃,我也不明白为什么M$要这么设计

01
/*
02
输
入: lpszExePath(LPCTSTR) - [in]可执行文件路径
03
输
出: DWORD - Size:LangCodePage, FALSE:设置代码页失败
04
功
能: 设置相应的代码页
05
*/
06
DWORD
CQueryExeInfo::SetLangCodePage(
LPCTSTR
lpszExePath)
07
{
08
DWORD
dwVersionInfoLen
= GetExeVersionInfo(lpszExePath);
09
UINT
*
puCodeLangBuffer;
10
DWORD
dwLangSet;
11
12
//
如果目标文件不存在,则dwLen为0
13
if
(!dwVersionInfoLen)
14
{
15
FreeVersionBuffer();
16
TRACE(_T(
"File
not found!\n"
));
17
return
FALSE;
18
}
19
20
//
在获取Info之前,需要先设置lang-codepage
21
TCHAR
szCodeLang[]
= _T(
"\\VarFileInfo\\Translation"
);
22
23
if
(!::VerQueryValue(m_lpVersionBuffer,
szCodeLang, (
LPVOID
*)&puCodeLangBuffer,
&m_uSizeOfData))
24
{
25
TRACE(_T(
"Query
Code page faild. Error Code is %d\n"
),
::GetLastError());
26
FreeVersionBuffer();
27
return
FALSE;
28
}
29
30
//
高字节段为Lang-Id,低字节段为Code-Page
31
dwLangSet
= MAKELONG(HIWORD(puCodeLangBuffer[0]), LOWORD(puCodeLangBuffer[0]));
32
33
return
dwLangSet;
34
}
有了代码页的数据,就可以获取相应的信息了,比如我们要获取CompanyName

01
/*
02
输
入: lpszExePath(LPCTSTR) - [in]可执行文件路径
03
sCompanyName(CString&)
- [out]接受CompanyName
04
输
出: BOOL - TRUE:成功, FALSE:失败
05
功
能: 获取目标文件的CompanyName
06
*/
07
BOOL
CQueryExeInfo::QueryExeCompanyName(
LPCTSTR
lpszExePath,
CString& sCompanyName)
08
{
09
DWORD
dwLangSetRet
= SetLangCodePage(lpszExePath);
10
11
if
(!dwLangSetRet)
12
{
13
FreeVersionBuffer();
14
return
FALSE;
15
}
16
17
//
lang-codepage必须以hex形式表示
18
TCHAR
szText[MAX_STRINGTABLE_LEN]
= {_T(
'\0'
)};
19
_stprintf(szText,
_T(
"\\StringFileInfo\\%08x\\CompanyName"
),
dwLangSetRet);
20
21
ASSERT(m_uSizeOfData);
22
23
//
OS自动为分配内存空间,并自动负责释放
24
if
(!::VerQueryValue(m_lpVersionBuffer,
szText, &m_szInfoKey, &m_uSizeOfData))
25
{
26
TRACE(_T(
"Query
Company Name Failed. Error Code is %d\n"
),
::GetLastError());
27
FreeVersionBuffer();
28
return
FALSE;
29
}
30
31
sCompanyName.Format(_T(
"%s"
),
(
LPCTSTR
)m_szInfoKey);
32
FreeVersionBuffer();
33
34
return
TRUE;
35
}
这里我们多次用到了FreeVersionBuffer,这个函数用于清理缓存区。

m_lpVersionBuffer需要我们自己释放,而m_uSizeOfData需要清零(zero out)

01
/*
02
输
入: -
03
输
出: -
04
功
能: 清理VersinoInfo占用的内存空间
05
*/
06
void
CQueryExeInfo::FreeVersionBuffer()
07
{
08
if
(m_lpVersionBuffer
!= NULL)
09
{
10
delete
[]
m_lpVersionBuffer;
11
m_lpVersionBuffer
=NULL;
12
}
13
14
if
(m_szInfoKey
!= NULL)
15
m_szInfoKey
= NULL;
16
17
m_uSizeOfData
= 0;
18
}
如果我们要获取文件版本,那么该怎么做呢?

仔细分析下上面获取CompanyName的代码,发现指定类型只需要酱紫的代码

1
//
lang-codepage必须以hex形式表示
2
TCHAR
szText[MAX_STRINGTABLE_LEN]
= {_T(
'\0'
)};
3
_stprintf(szText,
_T(
"\\StringFileInfo\\%08x\\[string-keyword]"
),
dwLangSetRet);
4
5
ASSERT(m_uSizeOfData);
里面的string-keyword指定了CompanyName、Fileversion这样的字段。关于详细的字段列表,请在MSDN中以String Structure为关键字搜索

而我们获取FileVersion的实现如下:

01
/*
02
输
入: lpszExePath(LPCTSTR) - [in]可执行文件路径
03
sFileVersion(CString&)
- [out]接受FileVersion
04
输
出: BOOL - TRUE:成功, FALSE:失败
05
功
能: 获取目标文件的FileVersion
06
*/
07
BOOL
CQueryExeInfo::QueryExeFileVersion(
LPCTSTR
lpszExePath,
CString& sFileVersion)
08
{
09
DWORD
dwLangSetRet
= SetLangCodePage(lpszExePath);
10
11
if
(!dwLangSetRet)
12
{
13
FreeVersionBuffer();
14
return
FALSE;
15
}
16
17
//
lang-codepage必须以hex形式表示
18
TCHAR
szText[MAX_STRINGTABLE_LEN]
= {_T(
'\0'
)};
19
_stprintf(szText,
_T(
"\\StringFileInfo\\%08x\\FileVersion"
),
dwLangSetRet);
20
21
ASSERT(m_uSizeOfData);
22
23
//
OS自动为分配内存空间,并自动负责释放
24
if
(!::VerQueryValue(m_lpVersionBuffer,
szText, &m_szInfoKey, &m_uSizeOfData))
25
{
26
TRACE(_T(
"Query
FileVersion Failed. Error Code is %d\n"
),
::GetLastError());
27
FreeVersionBuffer();
28
return
FALSE;
29
}
30
31
sFileVersion.Format(_T(
"%s"
),
(
LPCTSTR
)m_szInfoKey);
32
FreeVersionBuffer();
33
34
return
TRUE;
35
}
如你所见,大部分代码其实都一样。

其实,我们完全可以只写一个QueryExeInfo,然后设置一个Info的枚举类型,将枚举类型和保存string-keyword的TCHAR*数组形成映射关系。

只是,这样可能导致可读性下降

还要注意的是,FileVersion的那几个API需要显式指定version.lib

头文件如下

view
source

print?

01
#pragma
once
02
03
#pragma
comment(lib, "version.lib")
04
05
class
CQueryExeInfo
:
public
CObject
06
{
07
public
:
08
CQueryExeInfo();
09
virtual
~CQueryExeInfo();
10
HICON
QueryExeIcon(
LPCTSTR
lpszExePath);
11
static
void
FreeIconBuffer(
HICON
hIcon);
12
BOOL
QueryExeCompanyName(
LPCTSTR
lpszExePath,
CString& sCompanyName);
13
BOOL
QueryExeFileVersion(
LPCTSTR
lpszExePath,
CString& sFileVersion);
14
15
protected
:
16
DWORD
GetExeVersionInfo(
LPCTSTR
lpszExePath);
17
void
FreeVersionBuffer();
18
DWORD
SetLangCodePage(
LPCTSTR
lpszExePath);
19
20
protected
:
21
LPVOID
m_lpVersionBuffer;
22
LPVOID
m_szInfoKey;
23
UINT
m_uSizeOfData;
24
};
ok,至此功能就完成了,详细代码请翻阅SRC
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: