[ShellExtension]属性页扩展-IShellPropSheetExt实现

本文介绍如何使用ATL简单对象创建一个名为'ShellPropSheetExt'的工程,实现IShellPropSheetExt和IShellExtInit接口,为所有类型的文件属性添加一个显示文件完整路径的新页面。内容包括资源设置、注册表项添加以及最终实现的效果展示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

还是先亮源码下载地址:https://git.oschina.net/peterxiang/template_IShellPropSheetExt

以下所讲与提供下载的源码不同,但都是一个模子出来的。

下面代码实现的是在所有类型的文件的属性中添加一页,用于显示该文件的完整路径。支持同时选择多个文件并查看。

首先创建一个名为“ShellPropSheetExt”的工程,并添加一个简称为“FileFullPath”的“ATL简单对象”,如果不清楚的请看该文章:
[ShellExtension]图标扩展-IShellIconlayIdentifier实现
这篇文章的开头讲解了,我就不再赘述了。

现在开始讲解如何继承实现“IShellPropSheetExt”和“IShellExtInit”接口来达成目的。

// FileFullPath.h : CFileFullPath 的声明

#pragma once
#include "resource.h"       // 主符号
#include "ShellPropSheetExt_i.h"

#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Windows CE 平台(如不提供完全 DCOM 支持的 Windows Mobile 平台)上无法正确支持单线程 COM 对象。定义 _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA 可强制 ATL 支持创建单线程 COM 对象实现并允许使用其单线程 COM 对象实现。rgs 文件中的线程模型已被设置为“Free”,原因是该模型是非 DCOM Windows CE 平台支持的唯一线程模型。"
#endif

# include <vector>//引入头文件,会使用的vector

using namespace ATL;

// CFileFullPath

class ATL_NO_VTABLE CFileFullPath :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CFileFullPath, &CLSID_FileFullPath>,
	public IDispatchImpl<IFileFullPath, &IID_IFileFullPath, &LIBID_ShellPropSheetExtLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
	public IShellExtInit,//实现初始化接口,用于接收数据
	public IShellPropSheetExt//实现属性页接口
{
public:
	CFileFullPath()
	{
	}

DECLARE_REGISTRY_RESOURCEID(IDR_FILEFULLPATH)

BEGIN_COM_MAP(CFileFullPath)
	COM_INTERFACE_ENTRY(IFileFullPath)
	COM_INTERFACE_ENTRY(IDispatch)
	COM_INTERFACE_ENTRY(IShellExtInit)//添加入口点
	COM_INTERFACE_ENTRY(IShellPropSheetExt)//添加入口点
END_COM_MAP()

	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease()
	{
	}	
	
	//用于保存文件路径
	std::vector<std::wstring> m_vtFilePath;

	//实现IShellExtInit接口
	virtual HRESULT STDMETHODCALLTYPE Initialize(_In_opt_ PCIDLIST_ABSOLUTE pidlFolder, _In_opt_ IDataObject *pdtobj, _In_opt_ HKEY hkeyProgID);

	//实现IShellPropSheetExt接口
	virtual HRESULT STDMETHODCALLTYPE AddPages(_In_ LPFNSVADDPROPSHEETPAGE pfnAddPage, _In_ LPARAM lParam);

	virtual HRESULT STDMETHODCALLTYPE ReplacePage(_In_ EXPPS uPageID, _In_ LPFNSVADDPROPSHEETPAGE pfnReplaceWith, _In_ LPARAM lParam);

public:
	//消息响应函数,在类中必须为静态的
	static BOOL CALLBACK PropPageDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

	static UINT CALLBACK PropPageCallbackProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGEW ppsp);

	static BOOL OnInitDialog(HWND hwnd, LPARAM lParam);
};

OBJECT_ENTRY_AUTO(__uuidof(FileFullPath), CFileFullPath)

// FileFullPath.cpp : CFileFullPath 的实现

#include "stdafx.h"
#include "FileFullPath.h"

# include "dllmain.h"
// CFileFullPath

HRESULT STDMETHODCALLTYPE CFileFullPath::Initialize(_In_opt_ PCIDLIST_ABSOLUTE pidlFolder, _In_opt_ IDataObject *pdtobj, _In_opt_ HKEY hkeyProgID)
{
	FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
	STGMEDIUM stg = { TYMED_HGLOBAL };
	HDROP hDrop;

	//在数据对象中查找CF_HDROP类型数据
	if (FAILED(pdtobj->GetData(&fmt, &stg)))
	{//没有该数据
		return E_INVALIDARG;
	}

	//获取指向实际数据的指针
	hDrop = (HDROP)GlobalLock(stg.hGlobal);

	//检查
	if (NULL == hDrop)
	{
		return E_INVALIDARG;
	}

	HRESULT hr = S_OK;
	do
	{
		//有效性检查,保证最少有一个文件名
		UINT uNumFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
		if (0 == uNumFiles)
		{
			hr = E_INVALIDARG;
			break;
		}

		WCHAR tFilePath[MAX_PATH];
		//取得所有所有文件的文件名
		for (UINT i = 0; i < uNumFiles; i++)
		{
			if (0 != DragQueryFileW(hDrop, i, tFilePath, MAX_PATH))
			{
				//存放如文件列表中
				m_vtFilePath.push_back(tFilePath);
			}
			else
			{//有错误
				hr = E_INVALIDARG;
				break;
			}
		}
	} while (0);

	//释放资源
	GlobalUnlock(stg.hGlobal);
	ReleaseStgMedium(&stg);

	//检查要现实的属性页是否超出限制
	if (m_vtFilePath.size() > MAXPROPPAGES)
	{
		m_vtFilePath.resize(MAXPROPPAGES);
	}
	else if (m_vtFilePath.size() == 0)
	{//如果没有文件,则不继续执行
		hr = E_FAIL;
	}

	return hr;
}

HRESULT STDMETHODCALLTYPE CFileFullPath::AddPages(_In_ LPFNSVADDPROPSHEETPAGE pfnAddPage, _In_ LPARAM lParam)
{
	//为每一个文件添加属性页
	for each (std::wstring var in m_vtFilePath)
	{
		WCHAR szPageTitle[MAX_PATH];
		//拷贝路径
		lstrcpyW(szPageTitle, var.c_str());
		//移除目录
		PathStripPathW(szPageTitle);
		//移除扩展名
		PathRemoveExtensionW(szPageTitle);
		//截取前24个字符
		szPageTitle[24] = '\0';
		szPageTitle[23] = '.';
		szPageTitle[22] = '.';

		//创建属性页
		PROPSHEETPAGEW psp;
		psp.dwSize = sizeof(PROPSHEETPAGE);
		psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_DEFAULT | PSP_USEICONID | PSP_USECALLBACK;
		psp.hInstance = _AtlBaseModule.GetModuleInstance();
		psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE_MEDIUM);//通过资源加载属性页模版
		psp.pszIcon = MAKEINTRESOURCE(IDI_ICON1);//通过资源加载图标
		psp.pszTitle = szPageTitle;//标题
		psp.pfnDlgProc = (DLGPROC)PropPageDlgProc;//回调函数
		psp.lParam = (LPARAM)_wcsdup(var.c_str());//传入参数为文件路径
		psp.pfnCallback = (LPFNPSPCALLBACKW)PropPageCallbackProc;//回调函数
		psp.pcRefParent = (UINT*)&_AtlModule.m_nLockCnt;

		HPROPSHEETPAGE hPage = CreatePropertySheetPageW(&psp);
		if (NULL != hPage)
		{
			//添加到属性页窗口
			if (!pfnAddPage(hPage, lParam))
			{
				//添加失败的话需要主动删除
				DestroyPropertySheetPage(hPage);
			}
		}
	}
	return S_OK;
}
HRESULT STDMETHODCALLTYPE CFileFullPath::ReplacePage(_In_ EXPPS uPageID, _In_ LPFNSVADDPROPSHEETPAGE pfnReplaceWith, _In_ LPARAM lParam)
{//没有使用,直接返回
	return E_NOTIMPL;
}

BOOL CFileFullPath::PropPageDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{//响应页面事件
	BOOL bRet = FALSE;
	switch (uMsg)
	{
	case WM_INITDIALOG://属性页初始化消息
		bRet = OnInitDialog(hwnd, lParam);
		break;
	}

	return bRet;
}

UINT CFileFullPath::PropPageCallbackProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGEW ppsp)
{//在页面创建或销毁的时候被调用
	if (PSPCB_RELEASE == uMsg)
	{//在释放时调用
		//释放申请的字符串空间
		free((void*)ppsp->lParam);
	}

	//调用成功
	return 1;
}

BOOL CFileFullPath::OnInitDialog(HWND hwnd, LPARAM lParam)
{//初始化
	//获取传入的参数
	PROPSHEETPAGE* ppsp = (PROPSHEETPAGE*)lParam;
	LPCWSTR	szFile = (LPCWSTR)ppsp->lParam;
	
	//设置窗口中发的CEdit控件,显示文件路径
	HWND edit = GetDlgItem(hwnd, IDC_EDIT);
	SetWindowTextW(edit, szFile);
	
	return TRUE;
}

IDD_PROPPAGE_MEDIUM为添加的属性页资源。
IDI_ICON1为添加的图标资源。
IDC_EDIT为IDD_PROPPAGE_MEDIUM中的一个CEdit控件,用于显示文件路径

这里写图片描述
这里写图片描述

最后就是如图标扩展文章中一样,添加注册表项,将该程序注册在所有类型文件下,详细可看图标扩展的文章,这里就不再赘述。

HKCR
{
	NoRemove * 
	{
        NoRemove shellex 
        {
            NoRemove PropertySheetHandlers                
			{ 
				ForceRemove {1469FE7B-56CD-40BC-BF30-10A8B8009C63} = s 'FileFullPath Class'
			}            
		}     
	}
}

实现效果如下。
这里写图片描述

到此,功能已经实现,如果还有不明白的地方请留言,本文有许多不足的地方请指正,谢谢。

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值