静默调用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 ...
随机推荐
- 偏前端-vue.js学习之路初级(二)组件化构建
vue.js 组件化构建 组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型.自包含和通常可复用的组件构建大型应用.仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树: ...
- $.ajax(),$.get(),$.post()的区别,以及一些参数注意规则
$.ajax()方法和$.get(),$.post()方法的对比 $.ajax()方法是最完整的写法,可以完成所有的ajax请求(包含get类型和post类型) $.get()和$.post()都是简 ...
- jQuery 实现下载进度条
//ajax异步上传 $.ajax({ url: "${pageContext.request.contextPath }/upload", type: "POST&qu ...
- java 数组基础学习(一维二维数组)
1.一维数组 1>静态初始化:数据类型[ ] 变量名 = {元素} 例:int[ ] arr = {1,2} 动态初始化:数据类型[ ] 变量名 = new数据类型[数据长度] 例:int[ ] ...
- 和IDEA一样好用的go语言IDE:Goland
因为IDEA和Goland来自同一家非常有名的捷克公司:JetBrains 很好用,智能化程度高 最新版下载地址:http://www.jetbrains.com/go/?fromMenu 相关使用手 ...
- 原生js三级联动
<!DOCTYPE html> <html lang="en"> <head> <title> 三级联动 </title> ...
- 打豪车应用:uber详细攻略(附100元优步uber优惠码、uber优惠券、优步优惠码、优步优惠券)
在嘀嘀打车和快的打车交战热闹的时候,美国的打车应用uber进入中国.与在美国以个人司机注册做 Uber 司机为主的模式不同,Uber 在中国采用与租车公司合作.由租车公司提供车辆和司机的模式,同时中文 ...
- win32api 找不到指定的模块
pywin32 安装后 import win32api 出现ImportError: DLL load failed: 找不到指定的模块 解决方法: 拷贝 C:\Python26\Lib\site-p ...
- [WC2010]重建计划 长链剖分
[WC2010]重建计划 LG传送门 又一道长链剖分好题. 这题写点分治的人应该比较多吧,但是我太菜了,只会长链剖分. 如果你还不会长链剖分的基本操作,可以看看我的长链剖分总结. 首先一看求平均值最大 ...
- (转) PHP 开发者该知道的 5 个 Composer 小技巧
1. 仅更新单个库 只想更新某个特定的库,不想更新它的所有依赖,很简单: composer update foo/bar 此外,这个技巧还可以用来解决“警告信息问题”.你一定见过这样的警告信息: Wa ...