Windows特定文件添加右键菜单(三)

52 篇文章 2 订阅

Menu Handler实现右键菜单控制

微软提供了一系列强大并且灵活Handler供开发者使用,帮助我们实现更加多样化的功能;在shell执行命令或者工作之前,留给开发者一个机会来自定义实现;

关联文件类型或者可以指定文件类型的handler

这里写图片描述

没有关联文件类型但是可以被调用的Handler
这里写图片描述

我们的目标是添加右键菜单,也就是Shortcut menu Handler,在文件右键菜单显示之前,自定义添加右键菜单

我用的开发平台是VS2010,VS创建ATL COM组件的方法网上很多,也可以用其他工具,平台没有局限性
具体实现接口建议参考官方文档:
https://docs.microsoft.com/zh-cn/windows/desktop/shell/shortcut-menu-using-dynamic-verbs

实现menu handler主要实现两个接口IShellExtInit和IContextMenu

    // IShellExtInit
    STDMETHODIMP Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY);
    // IContextMenu
    STDMETHODIMP GetCommandString(UINT_PTR, UINT, UINT*, LPSTR, UINT);
    STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO);
    STDMETHODIMP QueryContextMenu(HMENU, UINT, UINT, UINT, UINT);

STDMETHODIMP Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY):初始化接口,可以获取右键目标,自定义是否需要添加右键功能,本例中通过此函数判断目标文件名,如果右键的是我们的对象则添加右键菜单,否则不添加

TDMETHODIMP CMenuHandler::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID)
{
    FORMATETC   fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    STGMEDIUM   stg = { TYMED_HGLOBAL };
    HDROP       hDrop = NULL;

    if (NULL == pDataObj)
        return E_INVALIDARG;

    // 从IDataObject接口中获取CF_HDROP数据
    if (FAILED(pDataObj->GetData(&fmt, &stg)))
        return E_INVALIDARG;

    // 获取指向实际数据的指针
    hDrop = (HDROP)GlobalLock(stg.hGlobal);
    if (hDrop == NULL)
    {
        ReleaseStgMedium(&stg);
        return E_INVALIDARG;
    }

    // 查询选择的文件数量
    UINT uFilesCount = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);

    if (1 != uFilesCount)
    {
        GlobalUnlock(stg.hGlobal);
        ReleaseStgMedium(&stg);
        return E_INVALIDARG;
    }

    // 将文件名称保存在变量中
    DragQueryFile(hDrop, 0, m_szFilename, MAX_PATH);

    // 判断名称是否包含GCCP5 以及是否存在exe
    TCHAR *sGCCP64 = _T("XXXX64.exe");
    TCHAR *sGCCP = _T("XXXX.exe");

    HRESULT state = E_INVALIDARG;
    if (_tcsstr(m_szFilename, sGCCP64) && (needCheckVersionInfo(TRUE)))
    {
        // 64位版本
        state = S_OK;
    }
    else if (_tcsstr(m_szFilename, sGCCP) && (needCheckVersionInfo(FALSE)))
    {
        // 32位版本
        state = S_OK;
    }
    else
    {
        // 不是目标程序  不添加右键
        state = E_INVALIDARG;
    }

    GlobalUnlock(stg.hGlobal);
    ReleaseStgMedium(&stg);
    return state;
}
  •  

STDMETHODIMP GetCommandString(UINT_PTR, UINT, UINT*, LPSTR, UINT):系统帮助信息接口,本例中不提供帮助信息直接返回

STDMETHODIMP CMenuHandler::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax)
{
    // 不实现 不提供帮助信息
    return E_NOTIMPL;
}

STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO):右键菜单响应接口,当点击右键菜单时执行,本例中我们直接调用第三方exe显示版本信息

STDMETHODIMP CMenuHandler::InvokeCommand(LPCMINVOKECOMMANDINFO pCmdInfo)
{
    if (0 != HIWORD(pCmdInfo->lpVerb))
        return E_INVALIDARG;

    switch (LOWORD(pCmdInfo->lpVerb))
    {
    case 0:
    {
        // dll注册是根据系统位数来的,但是调用exe位数得根据版本来
        DWORD dwRet, dwErr;
        LPTSTR pszOldVal = NULL;
        LPTSTR pszNewVal = NULL;
        BOOL fExist;

        pszOldVal = (LPTSTR)malloc(BUFSIZE*sizeof(TCHAR));

        if (NULL == pszOldVal)
        {
            return E_INVALIDARG;
        }

        //  获取当前环境变量
        dwRet = GetEnvironmentVariable(_T("Path"), pszOldVal, BUFSIZE);

        if (0 == dwRet)
        {
            // 获取失败 没有拿到该环境变量
            dwErr = GetLastError();
            if (ERROR_ENVVAR_NOT_FOUND == dwErr)
            {
                fExist = FALSE;
            }
        }
        else if (BUFSIZE < dwRet)
        {
            // Path路劲太长,缓存大小不够,从新根据dwRet大小分配
            pszOldVal = (LPTSTR)realloc(pszOldVal, dwRet*sizeof(TCHAR));
            if (NULL == pszOldVal)
            {
                return E_INVALIDARG;
            }
            // 获取环境变量
            dwRet = GetEnvironmentVariable(_T("Path"), pszOldVal, dwRet);

            if (!dwRet)
            {
                // 获取失败 没有拿到环境变量
                fExist = FALSE;
            }
            else
            {
                fExist = TRUE;
            }
        }
        else
        {
            // 直接获取环境变量
            fExist = TRUE;
        }

        // MessageBox(pCmdInfo->hwnd, pszOldVal, _T("Test"), MB_OK);

        // 追加Bin目录到环境变量
        pszNewVal = (LPTSTR)malloc((dwRet + MAX_PATH)*sizeof(TCHAR));

        if (NULL == pszNewVal)
        {
            if (!pszOldVal)
            {
                free(pszOldVal);
                pszOldVal = NULL;
            }
            return E_INVALIDARG;
        }

        _tcsncpy_s(pszNewVal, (dwRet + MAX_PATH), m_szBinname, _tcslen(m_szBinname));
        _tcscat_s(pszNewVal, (dwRet + MAX_PATH), _T(";"));
        _tcscat_s(pszNewVal, (dwRet + MAX_PATH), pszOldVal);

        // MessageBox(pCmdInfo->hwnd, pszNewVal, _T("Test"), MB_OK);

        if (!SetEnvironmentVariable(_T("Path"), pszNewVal))
        {
            // 环境变量设置失败 exe是无法启动的 直接退出
            if (!pszOldVal)
            {
                free(pszOldVal);
                pszOldVal = NULL;
            }
            if (!pszNewVal)
            {
                free(pszNewVal);
                pszNewVal = NULL;
            }
            return E_INVALIDARG;
        }

        try
        {
            UINT code = (UINT)ShellExecute(pCmdInfo->hwnd, _T("open"), m_szExename, _T(""), m_szBinname, SW_SHOWNORMAL);
        }
        catch (...)
        {

        }

        // 重置环境变量
        if (fExist)
        {
            if (!SetEnvironmentVariable(_T("Path"), pszOldVal))
            {
                return E_INVALIDARG;
            }
        }
        else
        {
            SetEnvironmentVariable(_T("Path"), NULL);
        }

        // 内存释放
        if (!pszOldVal)
        {
            free(pszOldVal);
            pszOldVal = NULL;
        }
        if (!pszNewVal)
        {
            free(pszNewVal);
            pszNewVal = NULL;
        }

        break;
    }
    default:
    {
        return E_INVALIDARG;
        break;
    }
    }
    return S_OK;
}

STDMETHODIMP QueryContextMenu(HMENU, UINT, UINT, UINT, UINT):右键菜单添加接口,本例中添加一个右键菜单

STDMETHODIMP CMenuHandler::QueryContextMenu(HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags)
{
    if (uFlags & CMF_DEFAULTONLY)
        return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);

    // 添加菜单
    InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, _T("查看版本信息"));

    return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1);
}

DLL创建完成之后,进行注册,测试效果OK,如果需要版本有安装程序,安装时候将dll注册

刚开始做的时候网上搜了很多人的例子,但是自己没有成功,之后将查看文档搞懂接口之后其实工作量不大,中间也遇到一些傻瓜式问题
1)dll注册的时候,64位注册64Dll,32位注册32位dll
2)32位dll无法注册成功:平台工具集要选择支持XP的平台工具集
3)dll被占用无法删除:可以进行重命名移动

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值