静默调用ShellContextMenu 实现QQ文件共享
我在CSDN提问题一直没人回复,一下午时间自己终于解决了问题
http://bbs.csdn.net/topics/391916381
现将过程录下
先说需求,我想实现的功能是 在程序中对文件调用百度网盘/qq的接口,发送给好友或上传到网盘,实现思路是右键菜单
现在我已经实现了在我的窗口中能够调出系统的右键菜单,并实现接口。
思路是 IShellFolder->ParseDisplayName 得到 文件到PIDL,IShellFolder->GetUIObjectOf() 得到IContextMenu 接口。
然后IContextMenu->QueryContextMenu() 得到菜单,再弹出菜单后,根据返回值调用 IContextMenu->InvokeCommand() 实现对文件的命令。
现在问题来了。
1. 在资源管理器中右键是有百度云盘的,但是在自己的窗口中没有。
2. 如何不弹出菜单,直接调用命令
3. 有可能右键菜单没有,但是QQShellExt YunShellExt 的COM接口还是存在的,有没有可能跳过ShellFolder->GetUiObjectOf 直接得到与云盘或QQ相关的 IContextMenu 或单独初始化一个。也就是有没有可能直接调用 QQShellExt 或 YunShellExt 的接口?
下面是我测试右键菜单的相关代码:
ShellContextMenu.h
#pragma once
class CShellContextMenu
{
public:
CShellContextMenu();
~CShellContextMenu(); public: bool ShowContextMenu(const CStringArray& files, HWND hWnd, LPPOINT pt); IShellFolder* GetDesktopFolder();
IShellFolder* GetParentFolder(LPCTSTR szFolder); CString GetDirectory(LPCTSTR szFile);
CString GetFileNameWithExt(LPCTSTR szFile); bool GetPidls(const CStringArray& files); private:
IContextMenu* GetContextMenuInterfaces(IShellFolder* pShellFolder, CArray<LPCITEMIDLIST>& idls);
bool InvokeCmd(IContextMenu* pContext, LPCTSTR szCmd, LPCTSTR szFolder);
bool InvokeCmd(IContextMenu* pContext, int nCmdSelection, LPCTSTR szFolder, LPPOINT pt);
bool ShowContextMenu(HWND hWnd, LPPOINT pt); void ReleaseIdls();
void ReleaseAll(); private:
CArray<LPCITEMIDLIST> m_idls; IShellFolder* m_pDesktopFolder;
IShellFolder* m_pParentFolder;
IContextMenu* m_pContextMenu;
IContextMenu2* m_pContextMenu2;
IContextMenu3* m_pContextMenu3; CString m_strParentFolder;
};
ShellContextMenu.cpp
#include "stdafx.h"
#include "ShellContextMenu.h" CShellContextMenu::CShellContextMenu()
{
m_pDesktopFolder = nullptr;
m_pParentFolder = nullptr; m_pContextMenu = nullptr;
m_pContextMenu2 = nullptr;
m_pContextMenu3 = nullptr;
} CShellContextMenu::~CShellContextMenu()
{
ReleaseAll();
} bool CShellContextMenu::ShowContextMenu(const CStringArray& files, HWND hWnd, LPPOINT pt)
{
ReleaseAll(); if (!GetPidls(files))
{
return false;
} return ShowContextMenu(hWnd, pt);
} IShellFolder* CShellContextMenu::GetDesktopFolder()
{
// 获取桌面指针
if (nullptr == m_pDesktopFolder)
{
if (S_OK != SHGetDesktopFolder(&m_pDesktopFolder))
{
return nullptr;
}
} return m_pDesktopFolder;
} IShellFolder* CShellContextMenu::GetParentFolder(LPCTSTR szFolder)
{
if (nullptr == m_pParentFolder)
{
auto pDesktop = GetDesktopFolder();
if (nullptr == pDesktop)
{
return nullptr;
} ULONG pchEaten = ;
LPITEMIDLIST pidl = nullptr;
DWORD dwAttributes = ;
auto hr = pDesktop->ParseDisplayName(nullptr, nullptr, (LPTSTR)szFolder, nullptr, &pidl, nullptr);
if (S_OK != hr)
{
return nullptr;
} STRRET sRetName;
hr = pDesktop->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &sRetName);
if (S_OK != hr)
{
CoTaskMemFree(pidl);
return nullptr;
} hr = StrRetToBuf(&sRetName, pidl, m_strParentFolder.GetBuffer(_MAX_PATH), _MAX_PATH);
m_strParentFolder.ReleaseBuffer();
if (S_OK != hr)
{
CoTaskMemFree(pidl);
return nullptr;
} IShellFolder* pParentFolder = nullptr;
hr = pDesktop->BindToObject(pidl, nullptr, IID_IShellFolder, (void**)&pParentFolder);
if (S_OK != hr)
{
CoTaskMemFree(pidl);
return nullptr;
} CoTaskMemFree(pidl); m_pParentFolder = pParentFolder; } return m_pParentFolder;
} CString CShellContextMenu::GetDirectory(LPCTSTR szFile)
{
TCHAR szDrive[_MAX_DRIVE];
TCHAR szDir[_MAX_DIR];
TCHAR szFName[_MAX_FNAME];
TCHAR szExt[_MAX_EXT];
_tsplitpath_s(szFile, szDrive, szDir, szFName, szExt); CString strResult = szDrive;
strResult += szDir;
return strResult;
} CString CShellContextMenu::GetFileNameWithExt(LPCTSTR szFile)
{
TCHAR szDrive[_MAX_DRIVE];
TCHAR szDir[_MAX_DIR];
TCHAR szFName[_MAX_FNAME];
TCHAR szExt[_MAX_EXT];
_tsplitpath_s(szFile, szDrive, szDir, szFName, szExt); CString strResult = szFName;
strResult += szExt;
return strResult;
} bool CShellContextMenu::GetPidls(const CStringArray& files)
{
ReleaseIdls(); if (files.IsEmpty())
{
return false;
} auto pParentFolder = GetParentFolder(GetDirectory(files[]));
if (nullptr == pParentFolder)
{
return false;
} for (int i = ; i < files.GetSize(); i++)
{
CString strFile = GetFileNameWithExt(files[i]); ULONG pchEaten = ;
LPITEMIDLIST pidl = nullptr;
DWORD dwAttributes = ;
auto hr = pParentFolder->ParseDisplayName(nullptr, nullptr, (LPTSTR)(LPCTSTR)strFile, &pchEaten, &pidl, &dwAttributes);
if (S_OK != hr)
{
continue;
} m_idls.Add(pidl); } return !m_idls.IsEmpty(); } IContextMenu* CShellContextMenu::GetContextMenuInterfaces(IShellFolder* pShellFolder, CArray<LPCITEMIDLIST>& idls)
{
IContextMenu* pResult = nullptr;
UINT refReversed = ;
auto hr = pShellFolder->GetUIObjectOf(nullptr, idls.GetSize(), idls.GetData(), IID_IContextMenu, &refReversed, (void**)&pResult);
if (S_OK != hr)
{
return nullptr;
} return pResult;
} bool CShellContextMenu::InvokeCmd(IContextMenu* pContext, LPCTSTR szCmd, LPCTSTR szFolder)
{
CMINVOKECOMMANDINFOEX info;
info.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
info.lpVerbW = szCmd;
info.lpDirectoryW = szFolder;
info.fMask = CMIC_MASK_UNICODE; return S_OK == pContext->InvokeCommand((CMINVOKECOMMANDINFO*)&info); } /// <summary>
/// 调用命令
/// </summary>
/// <param name="pContext"></param>
/// <param name="nCmdSelection"></param>
/// <param name="szFolder"></param>
/// <returns></returns>
bool CShellContextMenu::InvokeCmd(IContextMenu* pContext, int nCmdSelection, LPCTSTR szFolder, LPPOINT pt)
{
try
{
USES_CONVERSION;
CMINVOKECOMMANDINFOEX invoke;
memset(&invoke, , sizeof(CMINVOKECOMMANDINFOEX));
invoke.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
invoke.lpVerb = (LPCSTR)(nCmdSelection - CDM_FIRST);
invoke.lpDirectory = CT2CA(szFolder);
invoke.lpVerbW = (LPCTSTR)(nCmdSelection - CDM_FIRST);
invoke.lpDirectoryW = szFolder;
invoke.fMask = CMIC_MASK_UNICODE;
// invoke.ptInvoke.x = pt->x;
// invoke.ptInvoke.y = pt->y;
invoke.nShow = SW_SHOWNORMAL; auto hr = pContext->InvokeCommand((CMINVOKECOMMANDINFO*)&invoke);
return S_OK == hr; }
catch (...)
{
return false;
}
} bool CShellContextMenu::ShowContextMenu(HWND hWnd, LPPOINT pt)
{
if (m_idls.IsEmpty())
{
ReleaseAll();
return false;
} m_pContextMenu = GetContextMenuInterfaces(m_pParentFolder, m_idls);
if (nullptr == m_pContextMenu)
{
ReleaseAll();
return false;
} auto hMenu = ::CreatePopupMenu();
auto hr = m_pContextMenu->QueryContextMenu(hMenu, , CDM_FIRST, CDM_LAST, CMF_EXPLORE | CMF_NORMAL);
if (HRESULT_SEVERITY(hr) != SEVERITY_SUCCESS)
{
ReleaseAll();
return false;
} m_pContextMenu->QueryInterface(IID_IContextMenu2, (void**)&m_pContextMenu2);
m_pContextMenu->QueryInterface(IID_IContextMenu3, (void**)&m_pContextMenu3); auto nSel = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, pt->x, pt->y, hWnd, nullptr);
if ( == nSel)
{
auto error = ::GetLastError(); CString str;
::FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
str.GetBuffer(),
, NULL); str.ReleaseBuffer();
}
DestroyMenu(hMenu); // CString strCmd;
// hr = m_pContextMenu->GetCommandString(nSel, GCS_VALIDATE | GCS_VERB | GCS_UNICODE, nullptr, (char*)strCmd.GetBuffer(1024), 1024);
// strCmd.ReleaseBuffer();
//
// CString strHelpr;
// hr = m_pContextMenu->GetCommandString(nSel,GCS_VALIDATE | GCS_HELPTEXT | GCS_UNICODE, nullptr, (char*)strHelpr.GetBuffer(1024), 1024);
// strHelpr.ReleaseBuffer();
// if (S_OK == hr)
// {
// InvokeCmd(m_pContextMenu, strCmd, m_strParentFolder);
// } if (nSel > )
{
InvokeCmd(m_pContextMenu, nSel, m_strParentFolder, pt);
} ReleaseAll();
return true;
} void CShellContextMenu::ReleaseIdls()
{
for (int i = ; i < m_idls.GetSize(); i++)
{
if (nullptr != m_idls[i])
{
CoTaskMemFree((void*)m_idls[i]);
}
} m_idls.RemoveAll();
} void CShellContextMenu::ReleaseAll()
{
ReleaseIdls(); #define _ReleaseComPtr(p) \
if (nullptr != (p)){(p)->Release(); (p) = nullptr;} _ReleaseComPtr(m_pContextMenu3);
_ReleaseComPtr(m_pContextMenu2);
_ReleaseComPtr(m_pContextMenu);
_ReleaseComPtr(m_pParentFolder);
_ReleaseComPtr(m_pDesktopFolder); #undef _ReleaseComPtr
}
call
void CTestContextMenuCppDlg::OnRButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CShellContextMenu cxMenu; CStringArray ar;
ar.Add(_T("D:\\a.txt")); ClientToScreen(&point);
cxMenu.ShowContextMenu(ar, GetSafeHwnd(), &point); CDialogEx::OnRButtonUp(nFlags, point);
}
想要实现的形式:
CStringArray ar;
ar.Add(_T("D:\\a.txt"));
QQExt ext;
ext.发给好友(ar);// 调用右键菜单 发给好友
然后,我开始一边研究一边等待CSDN上能有人帮我解决这个问题。
更新1:
以上代码其实完全从 http://www.jackspace.cn/html/0528745226.html 抄来的,说是国外人写的,原文没找到。
更新2:
又找到这篇文章
http://bcbjournal.org/articles/vol4/0006/Using_the_shell_context_menu.htm
说系统菜单中的以下动作可以直接调用
Verb
openas
cut
copy
paste
link
delete
properties
Explore
find
COMPRESS
UNCOMPRESS
更新3:
#define CLSID_QQ_EXT "{53D2405C-48AB-4C8A-8F59-CE0610F13BBC}"
::CoInitialize(nullptr);
CLSID clsid;
CLSIDFromString(_T(CLSID_QQ_EXT), &clsid);
IContextMenu* pContextMenu = nullptr;
auto hr = CoCreateInstance(clsid, nullptr, CLSCTX_ALL, IID_IContextMenu, (void**)&pContextMenu);
if (S_OK != hr)
{
return false;
}
HMENU hMenu = CreatePopupMenu();
hr = pContextMenu->QueryContextMenu(hMenu, , CDM_FIRST, CDM_LAST, CMF_EXPLORE);
if (HRESULT_SEVERITY(hr) != SEVERITY_SUCCESS)
{
return false;
}
auto nSel = ::TrackPopupMenuEx(hMenu, TPM_RETURNCMD, pt->x, pt->y, hWnd, nullptr);
这段代码可以跑得通,并且弹出一个空菜单,所以问题就是怎么给 QQExt一个文件,或者pidl
更新4:
m_pContextMenu = GetContextMenuInterfaces(m_pParentFolder, m_idls);
if (nullptr == m_pContextMenu)
{
ReleaseAll();
return false;
} auto pContextMenu = m_pContextMenu; auto hr = m_pContextMenu->QueryInterface(IID_IContextMenu2, (void**)&m_pContextMenu2);
if (S_OK == hr)
{
pContextMenu = m_pContextMenu2;
} hr = pContextMenu->QueryInterface(IID_IContextMenu3, (void**)&m_pContextMenu3);
if (S_OK == hr)
{
pContextMenu = m_pContextMenu3;
} auto hMenu = ::CreatePopupMenu();
hr = pContextMenu->QueryContextMenu(hMenu, , CDM_FIRST, CDM_LAST, CMF_EXPLORE | CMF_NORMAL | CMF_ASYNCVERBSTATE);
if (HRESULT_SEVERITY(hr) != SEVERITY_SUCCESS)
{
ReleaseAll();
return false;
} auto nSel = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, pt->x, pt->y, hWnd, nullptr);
if ( == nSel)
{
auto error = ::GetLastError(); CString str;
::FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
str.GetBuffer(),
, NULL); str.ReleaseBuffer();
}
DestroyMenu(hMenu); CString strCmd;
hr = m_pContextMenu->GetCommandString(nSel - CDM_FIRST, GCS_VALIDATE | GCS_VERB | GCS_UNICODE, nullptr, (char*)strCmd.GetBuffer(), );
strCmd.ReleaseBuffer(); CString strHelpr;
hr = m_pContextMenu->GetCommandString(nSel - CDM_FIRST,GCS_VALIDATE | GCS_HELPTEXT | GCS_UNICODE, nullptr, (char*)strHelpr.GetBuffer(), );
strHelpr.ReleaseBuffer();
// if (S_OK == hr)
// {
// InvokeCmd(m_pContextMenu, strCmd, m_strParentFolder);
// }
//
if (nSel > )
{
InvokeCmd(pContextMenu, nSel, m_strParentFolder, pt);
}
此至,这条路完全跑通,明天开始整理相关代码,并测试这段代码对百度网盘的适应情况。
在CSDN盯了一下午,没有任何回复,沮丧。最后还是自己解决 。
静默调用ShellContextMenu 实现QQ文件共享的更多相关文章
- 腾讯QQAndroid API调用实例(QQ分享无需登录)
腾讯QQAndroid API调用实例(QQ分享无需登录) 主要分为两个步骤: 配置Androidmanifest.xml 修改activity里边代码 具体修改如下: 1.Activity代 ...
- 2019-5-21-C#-命令行如何静默调用-del-删除文件
title author date CreateTime categories C# 命令行如何静默调用 del 删除文件 lindexi 2019-05-21 11:32:28 +0800 2019 ...
- FileZilla命令行实现文件上传以及CreateProcess实现静默调用
应用需求: 用户在选择渲染作业时面临两种情况:一是选择用户远程存储上的文件:二是选择本地文件系统中的文件进行渲染.由于渲染任务是在远程主机上进行的,实际进行渲染时源文件也是在ftp目录 ...
- C#调用Mail发送QQ邮件
需要用到: 1.System.Net.Mail; 2.QQ邮箱的POP3/SMTP服务码 QQ邮箱的POP3/SMTP服务码获取方法: 1.打开qq邮箱: 2.进入设置页面-->账户:(往下翻) ...
- C# 命令行如何静默调用 del 删除文件
如果在 C# 命令行调用 del 删除文件,很多时候会提示是否需要删除,本文告诉大家如何调用命令行的时候静默删除 在C# 命令行 调用 del 删除文件的时候,会提示是否删除,通过在命令行加上 \Q ...
- 调用腾讯QQ启动
http://wpa.qq.com/msgrd?v=3&uin=88888888&site=qq&menu=yes
- net core调用MimeKit发送QQ邮件
一.在QQ邮箱内申请授权码,具体参考请官方文档 二.具体代码 public void TestSendMailDemo() { MimeMessage message = new MimeMessag ...
- html扩展调用qq聊天窗口
需要在官方给qq开通客服功能,使用相应的html代码,别人才能通过链接调用到该qq 官方生成调用链接 over!over!over!
- C# 软件绑定QQ群类开源放出
周天闲来无事写个公共类,可以添加到你们自己项目中限制必须加入你QQ群才可以使用. 代码简单,高手勿喷,有哪里不合理的请回帖让大家学习学习. using System; using System.Tex ...
随机推荐
- 请给出如下格式的date命令 例:11-02-26.再给出实现按周输出 比如:周六输出为6,请分别给出命令。
请给出如下格式的date命令 例:19-01-18.再给出实现按周输出 比如:周六输出为6,请分别给出命令. 解答: 方法1: [root@zhaokang ~]# date2019年 01月 17日 ...
- MySQL5.5安装(Windows版本)
1. 官网下载mysql5.5 下载地址:http://dev.mysql.com/downloads/mysql/5.5.html#downloads 2. 安装mysql5.5(安装之前,请关闭杀 ...
- Mysql双向同步热备份设置
1.环境描述. 主机:103.241.49.137(A) 主机:103.240.182.191(B) MYSQL 版本为5.1.112.授权用户.(本人比较懒,直接用的root 跳过这一步)A:mys ...
- 盒模型與BFC
盒模型 基本概念 什么是 CSS 盒模型?相信大部分人都能答出这个问题来,那就是 标准模型 + IE 模型 标准模型: IE 模型 很明显 在 标准盒子模型中,width 和 height 指的是内容 ...
- 先进先出算法(FIFO)——页面置换
原创 最近操作系统实习,写了先进先出算法(FIFO)的代码来实现页面置换. 题目阐述如下: 设计四:页面置换 设计目的: 加深对请求页式存储管理实现原理的理解,掌握页面置换算法. 设计内容: 设计一个 ...
- 20155301第十一周java课栈程序
20155301第十一周java课栈程序 内容一:后序表达式: abcde/-f+ 内容二:根据填充以下代码: import java.util.Scanner; public class MyDCT ...
- 20155306 2006-2007-2 《Java程序设计》第4周学习总结
20155306 2006-2007-2 <Java程序设计>第4周学习总结 教材学习内容总结 第六章 继承与多态 6.1 何谓继承 继承:面向对象中,为避免多个类间重复定义共同行为. 运 ...
- 20155320 《Java程序设计》实验三 敏捷开发与XP实践
20155320 <Java程序设计>实验三 敏捷开发与XP实践 实验内容 XP基础 XP核心实践 相关工具 (一)研究一下Code菜单 具体内容: 在IDEA中使用工具(Code-> ...
- Deepin Linux下的Metasploit安装及优化
前言 本文不限于Deepin Linux系统,类似的在ubuntu debian xubuntu等血统类似的Linux发行版中都可以使用这里方法来安装原生的metasploit 配置Kali Linu ...
- 图论-最短路径--3、SPFA算法O(kE)
SPFA算法O(kE) 主要思想是: 初始时将起点加入队列.每次从队列中取出一个元素,并对所有与它相邻的点进行修改,若某个相邻的点修改成功,则将其入队.直到队列为空时算法结束. 这个算 ...