Windows学习笔记-17(使用MFC读取程序信息并显示)

所用知识概要:

一、CListCtrl::InsertItem

1. 最常用版本

cpp 复制代码
int InsertItem(int nItem, LPCTSTR lpszItem);
int InsertItem(int nItem, LPCTSTR lpszItem, int nImage);
  • nItem :插入位置(索引,从0开始)。若为 -1 或大于项数,追加至末尾。

  • lpszItem:显示的文本。

  • nImage :图标在图像列表 中的索引(依赖 SetImageList 设置的列表)。

  • 返回值:新项的索引;失败返回 -1。

2. 带附加数据的版本

cpp 复制代码
int InsertItem(const LVITEM* pItem);

LVITEM 结构体包含项的全部属性:

cpp 复制代码
typedef struct {
    UINT mask;       // 哪些字段有效(LVIF_TEXT, LVIF_IMAGE, LVIF_PARAM...)
    int iItem;       // 索引
    int iSubItem;    // 子项列(通常0)
    UINT state;      // 状态
    UINT stateMask;  // 状态掩码
    LPTSTR pszText;  // 文本
    int cchTextMax;  // 缓冲区大小
    int iImage;      // 图标索引
    LPARAM lParam;   // 32/64位附加数据
    // ...(其他字段较少使用)
} LVITEM;

优势:一次性设置文本、图标、数据、状态等,效率更高。

示例(配合 SetImageList)

cpp 复制代码
// 假设已设置图像列表
m_list.SetImageList(&m_imgListSmall, LVSIL_SMALL);

// 方法1:直接插入带图标的项
m_list.InsertItem(0, _T("文档.txt"), 0);  // 图标索引0

// 方法2:使用LVITEM结构
LVITEM lvi = {0};
lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
lvi.iItem = 1;
lvi.pszText = _T("图片.jpg");
lvi.iImage = 1;
lvi.lParam = (LPARAM)pFileData;  // 绑定自定义数据
m_list.InsertItem(&lvi);

二、图像列表核心概念

  • 图像列表(Image List) :一组大小相同、索引有序的位图集合,存储在 CImageList 对象或 HIMAGELIST 句柄中。所有图标共享同一份资源,通过索引引用。

  • SetImageList:将图像列表"附加"到控件,控件绘制每一项时,根据该项指定的索引从图像列表中取出对应图标进行绘制。

三、常见控件的 SetImageList 方法

1. 列表视图(CListCtrl / ListView)

cpp 复制代码
CImageList* SetImageList(CImageList* pImageList, int nImageList);
  • nImageList 取值:

    • LVSIL_NORMAL:大图标模式(32×32 或自定义大小)

    • LVSIL_SMALL:小图标模式(16×16)

    • LVSIL_STATE:状态图标(覆盖在项左上角)

底层消息LVM_SETIMAGELIST

2. 树视图(CTreeCtrl / TreeView)

cpp 复制代码
CImageList* SetImageList(CImageList* pImageList, int nImageListType);
  • nImageListType 取值:

    • TVSIL_NORMAL:普通图标(展开/折叠状态共用)

    • TVSIL_STATE:状态图标(如复选框)

底层消息TVM_SETIMAGELIST

实践过程:

1、创建MFC应用程序,应用程序类型选择基于对话框,在静态库中使用MFC,生成的类选择为APP

2、对对话框的属性设置为:

边框:Resizing、系统菜单:True、最小化框:True、最大化框:True

同时移入List Control和一个按钮:

给List Control添加一个控件变量:

给按钮添加点击事件处理函数(类列表中选择为对话框的类):

并在对话框头文件中添加:

cpp 复制代码
#include <shlobj.h>     // IShellLink
#include <shellapi.h>   // ExtractIconEx
#include <atlbase.h>    // CComPtr



CImageList  m_imgList;      // 小图标图像列表

// 解析快捷方式:获取目标路径、图标路径及索引
BOOL ResolveShortcut(LPCTSTR lpszLinkFile, LPTSTR lpszTarget, int cchTarget,
                         LPTSTR lpszIconPath, int cchIconPath, int* pIconIndex);
// 添加项到列表
void AddApplicationToList(LPCTSTR lpszSelectedFile);

在对话框源文件中添加:
一定要注意:使用了16*16的图像列表imageList,即小图标,那么对列表控件的ModifyStyle()中要设置列表控件为LVS_SMALLICON即小图标视图,否则图标无法加载!

cpp 复制代码
BOOL CprogramreadDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

    m_list.ModifyStyle(LVS_TYPEMASK, LVS_SMALLICON);  // 设置为小图标视图
	// 初始化图像列表(16x16,32位色)
	m_imgList.Create(16, 16, ILC_COLOR32 | ILC_MASK, 20, 20);
	m_list.SetImageList(&m_imgList, LVSIL_SMALL);

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

//-------------------------------------------------------------------
// 解析快捷方式
//-------------------------------------------------------------------
// 函数:ResolveShortcut
// 功能:解析 Windows 快捷方式文件(.lnk)并获取其指向的目标路径、图标路径及图标索引
// 参数:
//   lpszLinkFile  - 输入,快捷方式文件(.lnk)的完整路径
//   lpszTarget    - 输出,指向接收目标文件路径的缓冲区
//   cchTarget     - 输入,lpszTarget 缓冲区的大小(字符数)
//   lpszIconPath  - 输出,指向接收图标文件路径的缓冲区
//   cchIconPath   - 输入,lpszIconPath 缓冲区的大小(字符数)
//   pIconIndex    - 输出,指向接收图标索引的整型变量
// 返回值:
//   TRUE  - 成功解析快捷方式
//   FALSE - 解析失败(可能是 COM 初始化失败、文件无效、接口获取失败等)
//-------------------------------------------------------------------
BOOL CprogramreadDlg::ResolveShortcut(LPCTSTR lpszLinkFile, LPTSTR lpszTarget, int cchTarget,
	LPTSTR lpszIconPath, int cchIconPath, int* pIconIndex)
{
	// 声明智能指针,使用 CComPtr 自动管理 COM 对象引用计数
	CComPtr<IShellLink> pShellLink;   // ShellLink 接口,用于操作快捷方式
	CComPtr<IPersistFile> pPersistFile; // IPersistFile 接口,用于从文件加载快捷方式数据

	// 初始化 COM 库,为当前线程启用 COM 支持
	// 注意:在 MFC 程序中,通常建议在 CWinApp::InitInstance() 中调用 AfxOleInit() 进行一次全局初始化
	HRESULT hr = CoInitialize(NULL);
	if (FAILED(hr))
		return FALSE; // COM 初始化失败,直接返回 FALSE

	// 创建 ShellLink 组件的实例
	// CLSID_ShellLink: ShellLink 组件的类标识符
	// CLSCTX_INPROC_SERVER: 指定在同一个进程内创建对象
	// IID_IShellLink: 请求 IShellLink 接口
	hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
		IID_IShellLink, (void**)&pShellLink);
	if (SUCCEEDED(hr)) // 如果成功创建 IShellLink 对象
	{
		// 查询 IPersistFile 接口,用于从文件加载数据
		hr = pShellLink->QueryInterface(IID_IPersistFile, (void**)&pPersistFile);
		if (SUCCEEDED(hr))
		{
			// 将快捷方式文件加载到 ShellLink 对象中
			// STGM_READ: 以只读方式打开文件
			hr = pPersistFile->Load(lpszLinkFile, STGM_READ);
			if (SUCCEEDED(hr))
			{
				// 获取快捷方式指向的目标文件路径
				// SLGP_UNCPRIORITY: 优先返回 UNC 格式的网络路径(如果适用)
				hr = pShellLink->GetPath(lpszTarget, cchTarget, NULL, SLGP_UNCPRIORITY);
				if (SUCCEEDED(hr))
				{
					// 获取快捷方式使用的图标信息
					int iIconIndex = 0; // 临时变量存储图标索引
					hr = pShellLink->GetIconLocation(lpszIconPath, cchIconPath, &iIconIndex);
					if (SUCCEEDED(hr))
					{
						*pIconIndex = iIconIndex; // 将图标索引输出到调用方提供的指针
					}
				}
			}
		}
	}

	// 清理当前线程的 COM 库
	CoUninitialize();

	// 如果整个过程中所有 hr 都成功,则 SUCCEEDED(hr) 返回 TRUE,否则返回 FALSE
	return SUCCEEDED(hr);
}

//-------------------------------------------------------------------
// 添加应用程序到列表
//-------------------------------------------------------------------
void CprogramreadDlg::AddApplicationToList(LPCTSTR lpszSelectedFile)
{
	TCHAR szTarget[MAX_PATH] = { 0 };
	TCHAR szFileName[MAX_PATH] = { 0 };
	TCHAR szIconPath[MAX_PATH] = { 0 };
	int nIconIndex = 0;

	// 判断是否为快捷方式
	CString ext = PathFindExtension(lpszSelectedFile);
	ext.MakeLower();

	if (ext == _T(".lnk"))
	{
		// 解析快捷方式
		if (!ResolveShortcut(lpszSelectedFile, szTarget, MAX_PATH,
			szIconPath, MAX_PATH, &nIconIndex))
		{
			AfxMessageBox(_T("无法解析快捷方式"));
			return;
		}

		// 若快捷方式未指定图标位置,则使用目标程序自身
		if (lstrlen(szIconPath) == 0)
		{
			lstrcpy(szIconPath, szTarget);
			nIconIndex = 0;
		}
	}
	else // .exe
	{
		lstrcpy(szTarget, lpszSelectedFile);
		lstrcpy(szIconPath, lpszSelectedFile);
		nIconIndex = 0;
	}

	// 获取文件名(不含路径)
	TCHAR fname[_MAX_FNAME], extname[_MAX_EXT];
	_tsplitpath_s(szTarget, NULL, 0, NULL, 0, fname, _MAX_FNAME, extname, _MAX_EXT);
	lstrcpy(szFileName, fname);
	lstrcat(szFileName, extname);

	// 从图标路径提取图标
	HICON hIcon = NULL;
	SHFILEINFO sfi = { 0 };
	if (SHGetFileInfo(szIconPath, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_SMALLICON))
	{
		hIcon = sfi.hIcon;
	}

	// 添加到图像列表
	int iImage = m_imgList.Add(hIcon);
	CString stre;
	stre.Format(_T("添加图标,索引 = %d, 当前图像列表数量 = %d\n"), iImage, m_imgList.GetImageCount());
	AfxMessageBox(stre);

	// 插入列表项
	LVITEM lvi = { 0 };
	lvi.mask = LVIF_TEXT | LVIF_IMAGE;
	lvi.iItem = m_list.GetItemCount();
	lvi.iImage = iImage;
	lvi.pszText = szFileName;
	m_list.InsertItem(&lvi);
}

实际效果:成功提取文件信息并进行显示

相关推荐
人间打气筒(Ada)3 小时前
SQL Server 之创建和管理数据表
运维·服务器·数据库·windows·sql语句·sql server·windows server
专业开发者3 小时前
Wi-Fi 技术学习:802.11ax MU-PPDU的帧格式与字段解析全解
网络·学习
【 】4233 小时前
stm32江协科技笔记(更新至3-3led流水灯和蜂鸣器)
笔记·stm32
科技林总3 小时前
【系统分析师】7.7 统一建模语言
学习
.小小陈.3 小时前
Python基础语法详解4:函数、列表与元组全解析
开发语言·c++·python·学习
南夏一木子3 小时前
Vue学习 —— Axios异步通信
前端·vue.js·学习
编码者卢布3 小时前
【Azure App Service】32位 Windows App Service 最大能使用多少内存?
windows·microsoft·azure
charlie1145141913 小时前
SSH X11 转发排查与解决指南(Windows + Xming + Ubuntu)
arm开发·windows·笔记·ubuntu·ssh·个人开发·环境配置
skywalk81633 小时前
汉字学习汉语学习相关库hanzi-writer 、makeme hanzi和chinese-xinhua
学习·汉字