Windows右键菜单扩展

Windows右键菜单扩展

首先,需要建立ATL项目,按照如下项目创建工程。
在这里插入图片描述
创建好后把带PS删掉。
在这里插入图片描述
如何添加一个ATL简单对象,填类名,点添加。之后直接点完成。
在这里插入图片描述
在这里插入图片描述

点开ATLTestMenu.h,CATLTestMenu类须继承IContextMenu接口和IShellExtInit接口,并实现其虚方法。
需要加:两个头文件,继承的两个接口,COM_INTERFACE_ENTRY两个接口,4个需要实现的虚方法。
ATLTestMenu.h代码如下:

// ATLTestMenu.h: CATLTestMenu 的声明

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

#include "shlobj.h"
#include "comdef.h"

#include "ATLDemo_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

using namespace ATL;


// CATLTestMenu

class ATL_NO_VTABLE CATLTestMenu :
	public CComObjectRootEx<CComSingleThreadModel>,
	public CComCoClass<CATLTestMenu, &CLSID_ATLTestMenu>,
	public IDispatchImpl<IATLTestMenu, &IID_IATLTestMenu, &LIBID_ATLDemoLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
	public IContextMenu,
	public IShellExtInit
{
public:
	CATLTestMenu()
	{
	}

DECLARE_REGISTRY_RESOURCEID(106)


BEGIN_COM_MAP(CATLTestMenu)
	COM_INTERFACE_ENTRY(IATLTestMenu)
	COM_INTERFACE_ENTRY(IDispatch)
	COM_INTERFACE_ENTRY(IContextMenu)
	COM_INTERFACE_ENTRY(IShellExtInit)
END_COM_MAP()



	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease()
	{
	}

public:
    HRESULT STDMETHODCALLTYPE Initialize(
        /* [annotation][unique][in] */
        _In_opt_  PCIDLIST_ABSOLUTE pidlFolder,
        /* [annotation][unique][in] */
        _In_opt_  IDataObject* pdtobj,
        /* [annotation][unique][in] */
        _In_opt_  HKEY hkeyProgID);

    HRESULT STDMETHODCALLTYPE QueryContextMenu(
        /* [annotation][in] */
        _In_  HMENU hmenu,
        /* [annotation][in] */
        _In_  UINT indexMenu,
        /* [annotation][in] */
        _In_  UINT idCmdFirst,
        /* [annotation][in] */
        _In_  UINT idCmdLast,
        /* [annotation][in] */
        _In_  UINT uFlags);

    HRESULT STDMETHODCALLTYPE InvokeCommand(
        /* [annotation][in] */
        _In_  CMINVOKECOMMANDINFO* pici);

    HRESULT STDMETHODCALLTYPE GetCommandString(
        /* [annotation][in] */
        _In_  UINT_PTR idCmd,
        /* [annotation][in] */
        _In_  UINT uType,
        /* [annotation][in] */
        _Reserved_  UINT* pReserved,
        /* [annotation][out] */
        _Out_writes_bytes_((uType& GCS_UNICODE) ? (cchMax * sizeof(wchar_t)) : cchMax) _When_(!(uType& (GCS_VALIDATEA | GCS_VALIDATEW)), _Null_terminated_)  CHAR* pszName,
        /* [annotation][in] */
        _In_  UINT cchMax);

    WCHAR m_szFile[MAX_PATH] = { 0 };


};

OBJECT_ENTRY_AUTO(__uuidof(ATLTestMenu), CATLTestMenu)

初始化是右键时调用,可以判断是在空白处右键还是选中文件右键,获取选中的文件等操作。一旦返回E_INVALIDARG,后面的方法都不再继续调用了。
初始化通过后就开始创建菜单。
InvokeCommand是点击菜单项的相应函数。

ATLTestMenu.cpp代码如下:

// ATLTestMenu.cpp: CATLTestMenu 的实现

#include "pch.h"
#include "ATLTestMenu.h"


// CATLTestMenu

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

    //空白处右键,pdtobj是空指针
    if (pdtobj == nullptr)
    {
        return E_INVALIDARG;
    }

    // 在数据对象内查找CF_HDROP类型数据。
    // 如果没有数据,返回一个错误(“无效参数”)给Explorer。
    if (FAILED(pdtobj->GetData(&fmt, &stg)))
    {
        return E_INVALIDARG;
    }

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


    // 确保非NULL
    if (NULL == hDrop)
    {
        return E_INVALIDARG;
    }
    //选中文件个数
    UINT uNumFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);

    HRESULT hr = S_OK;

    if (0 == uNumFiles)
    {
        GlobalUnlock(stg.hGlobal);
        ReleaseStgMedium(&stg);
        return E_INVALIDARG;
    }

    // 取得第一个文件名,保存到 m_szFile
    if (0 == DragQueryFile(hDrop, 0, m_szFile, MAX_PATH))
        hr = E_INVALIDARG;


    GlobalUnlock(stg.hGlobal);
    ReleaseStgMedium(&stg);
    return hr;
}

HRESULT __stdcall CATLTestMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
    // 如果标识包含了 CMF_DEFAULTONLY,那么,什么都不做
    if (uFlags & CMF_DEFAULTONLY)
    {
        return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
    }
    InsertMenu(hmenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);//分割线
    InsertMenu(hmenu, indexMenu + 1, MF_STRING | MF_BYPOSITION, idCmdFirst + 88, _T("Willow"));
    InsertMenu(hmenu, indexMenu + 2, MF_STRING | MF_BYPOSITION, idCmdFirst + 99, _T("圆仔"));
    InsertMenu(hmenu, indexMenu + 3, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);//分割线 

    return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(99 + 1));

}

HRESULT __stdcall CATLTestMenu::InvokeCommand(CMINVOKECOMMANDINFO* pici)
{
    if (0 != HIWORD(pici->lpVerb))
    {
        return E_INVALIDARG;
    }

    // 取得命令索引,上面的+多少,就是多少,默认是0
    switch (LOWORD(pici->lpVerb))
    {
    case 88:
    {

        TCHAR szMsg[MAX_PATH + 32];
        wsprintf(szMsg, _T("被选中的文件:\n\n%s"), m_szFile);
        MessageBox(pici->hwnd, szMsg, _T("弹窗1"),

            MB_ICONINFORMATION);

        return S_OK;
    }
    break;
    case 99:
    {

        TCHAR szMsg[MAX_PATH + 32];
        wsprintf(szMsg, _T("被选中的文件:\n\n%s"), m_szFile);

        MessageBox(pici->hwnd, szMsg, _T("弹窗2"),

            MB_ICONINFORMATION);

        return S_OK;
    }
    break;
    default:
        return E_INVALIDARG;

        break;

    }
}

HRESULT __stdcall CATLTestMenu::GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pReserved, CHAR* pszName, UINT cchMax)
{
    return S_OK;
}

rgs文件是修改注册表的文件。approve键是加权限,HKCR下增加键是可以控制选中文件夹、指定类型文档、空白处等调用此dll。*是代表全部。
代码如下:

HKCR
{
	NoRemove CLSID
	{
		ForceRemove {25fdbd79-4c1e-4669-afc9-0b1da1253ffc} = s 'ATLTestMenu class'
		{
			ForceRemove Programmable
			InprocServer32 = s '%MODULE%'
			{
				val ThreadingModel = s 'Apartment'
			}
			TypeLib = s '{20aed5aa-6e0e-4f03-b95a-4a34191e341f}'
			Version = s '1.0'
		}
	}

	NoRemove *
	{
		NoRemove ShellEx
		{
			NoRemove ContextMenuHandlers
			{
				ForceRemove 'ATLTestMenu' = s '{25fdbd79-4c1e-4669-afc9-0b1da1253ffc}'
			}
		}
	}
}

HKLM
{
	NoRemove Software
	{
		NoRemove Microsoft
		{
			NoRemove Windows
			{
				NoRemove CurrentVersion
				{
					NoRemove Shell Extensions
					{
						NoRemove Approved
						{
							ForceRemove 'ATLTestMenu' = s '{25fdbd79-4c1e-4669-afc9-0b1da1253ffc}'
						}
					}
				}
			}
		}
	}
}

最后,将编译好的dll注册到系统中,电脑是64位的要编译x64的库,32位的编译x86的库。使用管理员权限运行cmd,输入如下命令行注册和反注册dll。
Regsvr32+空格+dll全路径
Regsvr32+空格 + /u+空格+dll全路径
操作成功或失败均有提示框。
在这里插入图片描述

下图是最终效果:
在这里插入图片描述
在这里插入图片描述

如果想获取右键空白处的路径,可以使用下面代码:

TCHAR szFileDir[MAX_PATH] = { 0 };
//pidlFolder是Initialize方法中第一个参数
SHGetPathFromIDList(pidlFolder, szFileDir);

有问题欢迎大家一起交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值