静默调用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

 1 #pragma once
 2 class CShellContextMenu
 3 {
 4 public:
 5     CShellContextMenu();
 6     ~CShellContextMenu();
 7 
 8 public:
 9 
10 
11     bool ShowContextMenu(const CStringArray& files, HWND hWnd, LPPOINT pt);
12 
13 
14     IShellFolder* GetDesktopFolder();
15     IShellFolder* GetParentFolder(LPCTSTR szFolder);
16 
17     CString GetDirectory(LPCTSTR szFile);
18     CString GetFileNameWithExt(LPCTSTR szFile);
19 
20     bool GetPidls(const CStringArray& files);
21 
22 private:
23     IContextMenu* GetContextMenuInterfaces(IShellFolder* pShellFolder, CArray<LPCITEMIDLIST>& idls);
24     bool InvokeCmd(IContextMenu* pContext, LPCTSTR szCmd, LPCTSTR szFolder);
25     bool InvokeCmd(IContextMenu* pContext, int nCmdSelection, LPCTSTR szFolder, LPPOINT pt);
26     bool ShowContextMenu(HWND hWnd, LPPOINT pt);
27 
28     void ReleaseIdls();
29     void ReleaseAll();
30 
31 private:
32     CArray<LPCITEMIDLIST> m_idls;
33 
34     IShellFolder* m_pDesktopFolder;
35     IShellFolder* m_pParentFolder;
36     IContextMenu* m_pContextMenu;
37     IContextMenu2* m_pContextMenu2;
38     IContextMenu3* m_pContextMenu3;
39 
40     CString m_strParentFolder;
41 };
View Code

 

ShellContextMenu.cpp

  1 #include "stdafx.h"
  2 #include "ShellContextMenu.h"
  3 
  4 
  5 CShellContextMenu::CShellContextMenu()
  6 {
  7     m_pDesktopFolder = nullptr;
  8     m_pParentFolder = nullptr;
  9 
 10     m_pContextMenu = nullptr;
 11     m_pContextMenu2 = nullptr;
 12     m_pContextMenu3 = nullptr;
 13 }
 14 
 15 
 16 CShellContextMenu::~CShellContextMenu()
 17 {
 18     ReleaseAll();
 19 }
 20 
 21 bool CShellContextMenu::ShowContextMenu(const CStringArray& files, HWND hWnd, LPPOINT pt)
 22 {
 23     ReleaseAll();
 24 
 25     if (!GetPidls(files))
 26     {
 27         return false;
 28     }
 29 
 30     return ShowContextMenu(hWnd, pt);
 31 }
 32 
 33 IShellFolder* CShellContextMenu::GetDesktopFolder()
 34 {
 35     // 获取桌面指针
 36     if (nullptr == m_pDesktopFolder)
 37     {
 38         if (S_OK != SHGetDesktopFolder(&m_pDesktopFolder))
 39         {
 40             return nullptr;
 41         }
 42     }
 43 
 44     return m_pDesktopFolder;
 45 }
 46 
 47 IShellFolder* CShellContextMenu::GetParentFolder(LPCTSTR szFolder)
 48 {
 49     if (nullptr == m_pParentFolder)
 50     {
 51         auto pDesktop = GetDesktopFolder();
 52         if (nullptr == pDesktop)
 53         {
 54             return nullptr;
 55         }
 56 
 57         ULONG pchEaten = 0;
 58         LPITEMIDLIST pidl = nullptr;
 59         DWORD dwAttributes = 0;
 60         auto hr = pDesktop->ParseDisplayName(nullptr, nullptr, (LPTSTR)szFolder, nullptr, &pidl, nullptr);
 61         if (S_OK != hr)
 62         {
 63             return nullptr;
 64         }
 65 
 66         STRRET sRetName;
 67         hr = pDesktop->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &sRetName);
 68         if (S_OK != hr)
 69         {
 70             CoTaskMemFree(pidl);
 71             return nullptr;
 72         }
 73 
 74         hr = StrRetToBuf(&sRetName, pidl, m_strParentFolder.GetBuffer(_MAX_PATH), _MAX_PATH);
 75         m_strParentFolder.ReleaseBuffer();
 76         if (S_OK != hr)
 77         {
 78             CoTaskMemFree(pidl);
 79             return nullptr;
 80         }
 81 
 82         IShellFolder* pParentFolder = nullptr;
 83         hr = pDesktop->BindToObject(pidl, nullptr, IID_IShellFolder, (void**)&pParentFolder);
 84         if (S_OK != hr)
 85         {
 86             CoTaskMemFree(pidl);
 87             return nullptr;
 88         }
 89 
 90         CoTaskMemFree(pidl);
 91 
 92         m_pParentFolder = pParentFolder;
 93 
 94     }
 95 
 96     return m_pParentFolder;
 97 }
 98 
 99 CString CShellContextMenu::GetDirectory(LPCTSTR szFile)
100 {
101     TCHAR szDrive[_MAX_DRIVE];
102     TCHAR szDir[_MAX_DIR];
103     TCHAR szFName[_MAX_FNAME];
104     TCHAR szExt[_MAX_EXT];
105     _tsplitpath_s(szFile, szDrive, szDir, szFName, szExt);
106 
107     CString strResult = szDrive;
108     strResult += szDir;
109     return strResult;
110 }
111 
112 CString CShellContextMenu::GetFileNameWithExt(LPCTSTR szFile)
113 {
114     TCHAR szDrive[_MAX_DRIVE];
115     TCHAR szDir[_MAX_DIR];
116     TCHAR szFName[_MAX_FNAME];
117     TCHAR szExt[_MAX_EXT];
118     _tsplitpath_s(szFile, szDrive, szDir, szFName, szExt);
119 
120     CString strResult = szFName;
121     strResult += szExt;
122     return strResult;
123 }
124 
125 bool CShellContextMenu::GetPidls(const CStringArray& files)
126 {
127     ReleaseIdls();
128 
129     if (files.IsEmpty())
130     {
131         return false;
132     }
133 
134     auto pParentFolder = GetParentFolder(GetDirectory(files[0]));
135     if (nullptr == pParentFolder)
136     {
137         return false;
138     }
139 
140     for (int i = 0; i < files.GetSize(); i++)
141     {
142         CString strFile = GetFileNameWithExt(files[i]);
143         
144         ULONG pchEaten = 0;
145         LPITEMIDLIST pidl = nullptr;
146         DWORD dwAttributes = 0;
147         auto hr = pParentFolder->ParseDisplayName(nullptr, nullptr, (LPTSTR)(LPCTSTR)strFile, &pchEaten, &pidl, &dwAttributes);
148         if (S_OK != hr)
149         {
150             continue;
151         }
152 
153         m_idls.Add(pidl);
154 
155     }
156 
157     return !m_idls.IsEmpty();
158     
159 }
160 
161 IContextMenu* CShellContextMenu::GetContextMenuInterfaces(IShellFolder* pShellFolder, CArray<LPCITEMIDLIST>& idls)
162 {
163     IContextMenu* pResult = nullptr;
164     UINT refReversed = 0;
165     auto hr = pShellFolder->GetUIObjectOf(nullptr, idls.GetSize(), idls.GetData(), IID_IContextMenu, &refReversed, (void**)&pResult);
166     if (S_OK != hr)
167     {
168         return nullptr;
169     }
170 
171     return pResult;
172 }
173 
174 bool CShellContextMenu::InvokeCmd(IContextMenu* pContext, LPCTSTR szCmd, LPCTSTR szFolder)
175 {
176     CMINVOKECOMMANDINFOEX info;
177     info.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
178     info.lpVerbW = szCmd;
179     info.lpDirectoryW = szFolder;
180     info.fMask = CMIC_MASK_UNICODE;
181 
182     return S_OK == pContext->InvokeCommand((CMINVOKECOMMANDINFO*)&info);
183 
184 }
185 
186 /// <summary>
187 /// 调用命令
188 /// </summary>
189 /// <param name="pContext"></param>
190 /// <param name="nCmdSelection"></param>
191 /// <param name="szFolder"></param>
192 /// <returns></returns>
193 bool CShellContextMenu::InvokeCmd(IContextMenu* pContext, int nCmdSelection, LPCTSTR szFolder, LPPOINT pt)
194 {
195     try
196     {
197         USES_CONVERSION;
198         CMINVOKECOMMANDINFOEX invoke;
199         memset(&invoke, 0, sizeof(CMINVOKECOMMANDINFOEX));
200         invoke.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
201         invoke.lpVerb = (LPCSTR)(nCmdSelection - CDM_FIRST);
202         invoke.lpDirectory = CT2CA(szFolder);
203         invoke.lpVerbW = (LPCTSTR)(nCmdSelection - CDM_FIRST);
204         invoke.lpDirectoryW = szFolder;
205         invoke.fMask = CMIC_MASK_UNICODE;
206         //    invoke.ptInvoke.x = pt->x;
207         //    invoke.ptInvoke.y = pt->y;
208         invoke.nShow = SW_SHOWNORMAL;
209 
210         auto hr = pContext->InvokeCommand((CMINVOKECOMMANDINFO*)&invoke);
211         return S_OK == hr;
212 
213     }
214     catch (...)
215     {
216         return false;
217     }
218 }
219 
220 bool CShellContextMenu::ShowContextMenu(HWND hWnd, LPPOINT pt)
221 {
222     if (m_idls.IsEmpty())
223     {
224         ReleaseAll();
225         return false;
226     }
227 
228     m_pContextMenu = GetContextMenuInterfaces(m_pParentFolder, m_idls);
229     if (nullptr == m_pContextMenu)
230     {
231         ReleaseAll();
232         return false;
233     }
234     
235     auto hMenu = ::CreatePopupMenu();
236     auto hr = m_pContextMenu->QueryContextMenu(hMenu, 0, CDM_FIRST, CDM_LAST, CMF_EXPLORE | CMF_NORMAL);
237     if (HRESULT_SEVERITY(hr) != SEVERITY_SUCCESS)
238     {
239         ReleaseAll();
240         return false;
241     }
242 
243     m_pContextMenu->QueryInterface(IID_IContextMenu2, (void**)&m_pContextMenu2);
244     m_pContextMenu->QueryInterface(IID_IContextMenu3, (void**)&m_pContextMenu3);
245 
246     auto nSel = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, pt->x, pt->y, hWnd, nullptr);
247     if (0 == nSel)
248     {
249         auto error = ::GetLastError();
250         
251         CString str;
252         ::FormatMessageW(
253             FORMAT_MESSAGE_ALLOCATE_BUFFER |
254             FORMAT_MESSAGE_FROM_SYSTEM |
255             FORMAT_MESSAGE_IGNORE_INSERTS,
256             NULL,
257             error,
258             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
259             str.GetBuffer(1024),
260             1024, NULL);
261 
262         str.ReleaseBuffer();
263     }
264     DestroyMenu(hMenu);
265 
266 //    CString strCmd;
267 //    hr = m_pContextMenu->GetCommandString(nSel, GCS_VALIDATE | GCS_VERB | GCS_UNICODE, nullptr, (char*)strCmd.GetBuffer(1024), 1024);
268 //    strCmd.ReleaseBuffer();
269 //
270 //    CString strHelpr;
271 //    hr = m_pContextMenu->GetCommandString(nSel,GCS_VALIDATE |  GCS_HELPTEXT | GCS_UNICODE, nullptr, (char*)strHelpr.GetBuffer(1024), 1024);
272 //    strHelpr.ReleaseBuffer();
273 //    if (S_OK == hr)
274 //    {
275 //        InvokeCmd(m_pContextMenu, strCmd, m_strParentFolder);
276 //    }
277 
278     if (nSel > 0)
279     {
280         InvokeCmd(m_pContextMenu, nSel, m_strParentFolder, pt);
281     }
282 
283     ReleaseAll();
284     return true;
285 }
286 
287 void CShellContextMenu::ReleaseIdls()
288 {
289     for (int i = 0; i < m_idls.GetSize(); i++)
290     {
291         if (nullptr != m_idls[i])
292         {
293             CoTaskMemFree((void*)m_idls[i]);
294         }
295     }
296 
297     m_idls.RemoveAll();
298 }
299 
300 void CShellContextMenu::ReleaseAll()
301 {
302     ReleaseIdls();
303 
304 #define _ReleaseComPtr(p) \
305     if (nullptr != (p)){(p)->Release(); (p) = nullptr;}
306 
307     _ReleaseComPtr(m_pContextMenu3);
308     _ReleaseComPtr(m_pContextMenu2);
309     _ReleaseComPtr(m_pContextMenu);
310     _ReleaseComPtr(m_pParentFolder);
311     _ReleaseComPtr(m_pDesktopFolder);
312 
313 #undef _ReleaseComPtr
314 }
View Code

 

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);
}
View Code

 

想要实现的形式:

    CStringArray ar;
    ar.Add(_T("D:\\a.txt"));
        QQExt ext;
        ext.发给好友(ar);// 调用右键菜单 发给好友
View Code

 

然后,我开始一边研究一边等待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, 0, 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);
View Code

这段代码可以跑得通,并且弹出一个空菜单,所以问题就是怎么给 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, 0, 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 (0 == 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(1024),
            1024, NULL);

        str.ReleaseBuffer();
    }
    DestroyMenu(hMenu);

    CString strCmd;
    hr = m_pContextMenu->GetCommandString(nSel - CDM_FIRST, GCS_VALIDATE | GCS_VERB | GCS_UNICODE, nullptr, (char*)strCmd.GetBuffer(1024), 1024);
    strCmd.ReleaseBuffer();

    CString strHelpr;
    hr = m_pContextMenu->GetCommandString(nSel - CDM_FIRST,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 > 0)
    {
        InvokeCmd(pContextMenu, nSel, m_strParentFolder, pt);
    }
View Code

 

此至,这条路完全跑通,明天开始整理相关代码,并测试这段代码对百度网盘的适应情况。

在CSDN盯了一下午,没有任何回复,沮丧。最后还是自己解决 。

posted @ 2016-03-15 17:47  杨海龙  阅读(836)  评论(0编辑  收藏  举报