zoukankan      html  css  js  c++  java
  • 静默调用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盯了一下午,没有任何回复,沮丧。最后还是自己解决 。

  • 相关阅读:
    Java:IO流之字符流缓冲区详解
    Java:IO流之字符流Reader、Writer详解
    Java:IO流之字节流InputStream、OutputStream详解
    iOS:Git分布式版本控制器系统
    Java:日历类、日期类、数学类、运行时类、随机类、系统类
    Java:泛型
    Java:静态导入
    Java:集合for高级循环遍历
    一个相当好的状态机(DFA, 确定有限状态机)的编码实现,相当简洁漂亮
    android 开发必用的开源库
  • 原文地址:https://www.cnblogs.com/hailong/p/5280429.html
Copyright © 2011-2022 走看看