静默调用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 ...
随机推荐
- 关于TCP传输的三次握手四次挥手策略
简单小总结: 一般为了能够准确无误地把数据送达目标处,TCP协议采用了三次握手策略.用TCP协议把数据包送出去后,TCP不会对传送后的情况置之不理,它一定会向对方确认是否成功送达. 注:握手过程中使用 ...
- 购物车功能:使用jQuery实现购物车全选反选,单选,商品增删,小计等功能
效果图: html: <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...
- shell的奇淫巧技--自动化脚本(sed命令)
使用场景:前段时间交易所项目需要在服务器上用到 根据websocket推送价格数据,在交易所内进行下单撤单处理,但是由于有多个交易对,在服务器上部署时候,略显繁琐.(撮合引擎同样有此问题,可以一并解决 ...
- mongo数据集合属性中存在点号(.)
基本知识点: 1.似乎mongo3.6之前不允许插入带点(.)或美元符号($)的键,但是当我使用mongoimport工具导入包含点的JSON文件时,它工作正常. 2.在使用spring-data-m ...
- [示例] 用代码设置 ListView 颜色 (只适用 Win 平台,无需修改官方源码)
如果可以使用代码随意设置 ListView 的颜色,而不用加载额外的 Style 及修改官方的源码,那该有多好?! 其实 Style 提供了很强了扩充性及可塑性,可以很容易的去操作它. 下面以 Lis ...
- IOS 可以连接 蓝牙BLE设备,但是无法发现服务(原创)
注:转载请标明文章来源,感谢支持作者劳动! 一.问题描述 用iphone手机上的nRF connect软件调试蓝牙通信. 1.nRF52蓝牙demo电路板,烧录一个SDK的程序,iphone手机可以成 ...
- 详解 Python3 正则表达式(三)
上一篇:详解 Python3 正则表达式(二) 本文翻译自:https://docs.python.org/3.4/howto/regex.html 博主对此做了一些批注和修改 ^_^ 模块级别的函数 ...
- python多进程详解和协程
1.由于python多线程适合于多IO操作,但不适合于cpu计算型工作,这时候可以通过多进程实现.python多进程简单实用 # 多进程,可以cpu保持一致,python多线程适合多io.对于高cpu ...
- vue手脚架安装和项目创建
一 node安装 1 如果不确定自己是否安装了node,可以在命令行工具内执行:node -v: 2如果执行结果显示:xx不是内部命令,说明你还没有安装node,node按爪给你地址 : http:/ ...
- 20155302 实验三 敏捷开发与XP实践
20155302 实验三 敏捷开发与XP实践 实验内容 XP基础 XP核心实践 相关工具 实验内容及步骤 (一)编码标准 在IDEA中使用工具(Code->Reformate Code)把代码重 ...