静默调用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 ...
随机推荐
- django的数据库操作-16
目录 增 1.save 2.create 查 1.基本查询 2.过滤查询 3. F对象 4. Q对象 5. 聚合函数 6. 排序 7. 关联查询 8. 关联+过滤查询 删 改 1. save 2. u ...
- Python学习:13.Python正则表达式
一.正则表达式简介 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配. Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式. 就其本质 ...
- PTA(Basic Level)-1076 Wifi密码
一 题目介绍: 现将 wifi 密码设置为下列数学题答案:A-1:B-2:C-3:D-4.本题就要求你写程序把一系列题目的答案按照卷子上给出的对应关系翻译成 wifi 的密码.这里简单假设每道 ...
- 基于 OpenResty 实现一个 WS 聊天室
基于 OpenResty 实现一个 WS 聊天室 WebSocket WebSocket 协议分析 WebSocket 协议解决了浏览器和服务器之间的全双工通信问题.在WebSocket出现之前,浏览 ...
- C# typeof() 和 GetType()区是什么
1.typeof(x)中的x,必须是具体的类名.类型名称等,不可以是变量名称. 2.GetType()方法继承自Object,所以C#中任何对象都具有GetType()方法,它的作用和typeof() ...
- 20155203 2016-2017-3 《Java程序设计》第4周学习总结
20155203 2016-2017-3 <Java程序设计>第4周学习总结 教材学习内容总结 1.父类和子类类似于集合和元素,不同的地方是子类可以拓展(extends)父类之外的方法. ...
- 对PostgreSQL数据库的hstore类型建立GisT索引的实验
磨砺技术珠矶,践行数据之道,追求卓越价值回到上一级页面:PostgreSQL基础知识与基本操作索引页 回到顶级页面:PostgreSQL索引页[作者 高健@博客园 luckyjackgao@g ...
- day 2 函数的嵌套
1.函数的嵌套 百度百科 2.产品如何运作 3.应用 1)版本1:打印1条线 #1.定义函数 def print_line(): print("-"*50) #2.调用函数 ...
- 【LG4841】城市规划
[LG4841]城市规划 题面 洛谷 题解 记\(t_i\)表示\(i\)个点的无向图个数,显然\(t_i=2^{C_i^2}\). 设\(f_i\)表示\(i\)个点的无向连通图个数,容斥一下,枚举 ...
- springboot 中controller 返回html界面或 jsp界面
参考链接:https://blog.csdn.net/qq_15260315/article/details/80907056 经尝试,返回html界面没问题,但是返回jsp界面是有问题的,just ...