如何在命名空间扩展中添加(启用)标准“发送到”上下文菜单选项

时间:2014-04-10 13:53:25

标签: windows winapi com windows-shell shell-extensions

我有一个命名空间扩展,它提供了服务器中文件/文件夹的虚拟视图。

IContextMenu::QueryContextMenu()我添加了一些自定义菜单项。

我还在IShellFolder::GetAttributesOf()中设置了几个SGAOF标志,以便在上下文菜单中获取重命名,删除和属性。

我有什么方法可以获得" 发送到"我的命名空间扩展中的项目的上下文菜单中的选项?一旦启用这些命令,我​​该如何处理?请指教。

这是我尝试的代码,因为Denis Anisimov建议

    const CLSID SendToCLSID = { 0x7BA4C740, 0x9E81, 0x11CF, { 0x99, 0xD3, 0x00, 0xAA, 0x00, 0x4A, 0xE8, 0x37 } };

    HRESULT CMyNSEContextMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder , IDataObject *pDataObj, HKEY  hkeyProgID )
    {
        OutputDebugString(L"CMyNSEContextMenu::Initialize\n");
        //Other initialization code
        ...
        ...

        if (_pdtobj)
        {
            _pdtobj->Release();
            _pdtobj = NULL;
        }

        _mpidlFolder = pidlFolder;
        _pdtobj = pDataObj;
        if (pDataObj)
        {
            _pdtobj->AddRef();
            CoCreateInstance(SendToCLSID, NULL, CLSCTX_INPROC_SERVER, IID_IContextMenu, (LPVOID*)&_pSendToMenu);
        }
        return S_OK;
    }

    HRESULT CMyNSEContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT  idCmdLast , UINT  uFlags )
    {
        OutputDebugString(L"CMyNSEContextMenu::QueryContextMenu\n");
        UNREFERENCED_PARAMETER(indexMenu);
        UNREFERENCED_PARAMETER(idCmdFirst);
        //Get File Name
        IShellItemArray *psia=NULL;
        HRESULT    hr;
        USHORT items = 0;

        //Adding other menu items


        AddMenuItem(hmenu, 
                    indexMenu++, 
                    idCmdFirst + MENUVERB_XXX, 
                    IDS_COMMAND_XXX, 
                    IDB_XXX);
        items++;

        IShellExtInit *pShellExtInitSendTo = NULL;

        _pSendToMenu->QueryInterface(IID_IShellExtInit, (LPVOID*)&pShellExtInitSendTo);
        pShellExtInitSendTo->Initialize(NULL, _pdtobj, 0); // your IDataObject with CFSTR_SHELLIDLIST format)
        hr = _pSendToMenu->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
        if (SUCCEEDED(hr))
        {
            items += HRESULT_CODE(hr);
        }

        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (USHORT)(items));
    }

    HRESULT CMyNSEContextMenu::HandleMenuMsg(
        UINT uMsg,
        WPARAM wParam,
        LPARAM lParam
        )
    {
        IContextMenu2 *pSendToMenu = NULL;
        _pSendToMenu->QueryInterface(IID_IContextMenu2, (LPVOID*)&pSendToMenu);

        return pSendToMenu->HandleMenuMsg(uMsg,wParam,lParam);
    }

    HRESULT CMyNSEContextMenu::HandleMenuMsg2(
        UINT uMsg,
        WPARAM wParam,
        LPARAM lParam,
        LRESULT *plResult
        )
    {
        IContextMenu3 *pSendToMenu = NULL;
        _pSendToMenu->QueryInterface(IID_IContextMenu3, (LPVOID*)&pSendToMenu);
        return pSendToMenu->HandleMenuMsg2(uMsg, wParam, lParam, plResult);
    }
HRESULT CMyNSEContextMenu::GetCommandString(UINT_PTR  idCmd , UINT uType , UINT *  pRes , LPSTR  pszName , UINT  cchMax )
{
    OutputDebugString(L"CMyNSEContextMenu::GetCommandString\n");

    return _pSendToMenu->GetCommandString(idCmd, uType, pRes, pszName, cchMax);

}

默认上下文菜单是作为GetUIObjectOf的一部分创建的。而MyNSEContextMenu类的实例是通过Classfactory。

HRESULT CMyNSEShellFolder::GetUIObjectOf(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
                                             REFIID riid, UINT * /* prgfInOut */, void **ppv)
{
    OutputDebugString(L"CMyNSEShellFolder::GetUIObjectOf\n");
    *ppv = NULL;
    HRESULT hr = E_NOINTERFACE;

    if (riid == IID_IContextMenu)
    {
        // The default context menu will call back for IQueryAssociations to determine the
        // file associations with which to populate the menu.
        DEFCONTEXTMENU const dcm = { hwnd, NULL, m_pidl, static_cast<IShellFolder2 *>(this),
                               cidl, apidl, NULL, 0, NULL };
        hr = SHCreateDefaultContextMenu(&dcm, riid, ppv);
    }   
    //Others
    ....
    ....
    else if (riid == IID_IQueryAssociations)
    {
            else
            {
                ASSOCIATIONELEMENT const rgAssocItem[] =
                {
                    { ASSOCCLASS_PROGID_STR, NULL, L"MyNSE_Type"},
                };
                hr = AssocCreateForClasses(rgAssocItem, ARRAYSIZE(rgAssocItem), riid, ppv);
            }
    }
    ...
    ...
    return hr;
}

//Called from the class factory     
HRESULT CMyNSEContextMenu_CreateInstance(REFIID riid, void **ppv)
{
    *ppv = NULL;
    CMyNSEContextMenu* pContextMenu = new (std::nothrow) CMyNSEContextMenu();
    HRESULT hr = pContextMenu ? S_OK : E_OUTOFMEMORY;
    if (SUCCEEDED(hr))
    {
        hr = pContextMenu->QueryInterface(riid, ppv);
        pContextMenu->Release();
    }
    return hr;
}

编写的相关注册表如下

HKEY_LOCAL_MACHINE,   L"Software\\Classes\\CLSID\\%s",                  szContextMenuClassID,    NULL,                   (LPBYTE)g_szExtTitle,       REG_SZ,
HKEY_LOCAL_MACHINE,   L"Software\\Classes\\CLSID\\%s\\InprocServer32",  szContextMenuClassID,    NULL,                   (LPBYTE)L"%s",              REG_SZ,
HKEY_LOCAL_MACHINE,   L"Software\\Classes\\CLSID\\%s\\InprocServer32",  szContextMenuClassID,    L"ThreadingModel",      (LPBYTE)L"Apartment",       REG_SZ,

        HKEY_LOCAL_MACHINE, L"Software\\Classes\\CLSID\\%s\\ProgID", szFolderViewImplClassID, NULL, (LPBYTE)L"MyNSE_Type", REG_SZ,

// For performance, only context menu verbs that register this are considered when the user double-clicks.
HKEY_CLASSES_ROOT,   L"CLSID\\%s\\ShellEx\\MayChangeDefaultMenu",                szContextMenuClassID, NULL,  (LPBYTE)L"",                  REG_SZ,
// register the context menu handler under the MyNSE_Type type.
HKEY_CLASSES_ROOT,   L"MyNSE_Type\\shellex\\ContextMenuHandlers\\%s",  szContextMenuClassID, NULL,  (LPBYTE)szContextMenuClassID, REG_SZ,

2 个答案:

答案 0 :(得分:1)

SendTo只是简单的shell扩展,它实现了IContextMenu(2,3)。 Windows 7中的扩展名为{7BA4C740-9E81-11CF-99D3-00AA004AE837}(请不要忘记在您要支持的其他Windows版本中检查正确的CLSID)。所以只需使用这样的东西:

function TMenuWithSentTo.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult;
const
  SendToCLSID: TGUID = '{7BA4C740-9E81-11CF-99D3-00AA004AE837}';
var
  ShellExtInit: IShellExtInit;
begin
  Result := 0;

  // Add you menu items here

  CoCreateInstance(SendToCLSID, nil, CLSCTX_INPROC_SERVER, IContextMenu, FSendToMenu);
  FSendToMenu.QueryInterface(IShellExtInit, ShellExtInit);
  ShellExtInit.Initialize(nil, FDataObject, 0); // your IDataObject with CFSTR_SHELLIDLIST format 
  Result := Result + FSendToMenu.QueryContextMenu(Menu, indexMenu, idCmdFirst, idCmdLast, uFlags);

  // Add you menu items here
end;

function TMenuWithSentTo.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult;
begin
  if IsMyCommand(lpici) then
    begin
      // Process your command here
      Result := S_OK;
    end
  else
    Result := FSendToMenu.InvokeCommand(lpici);
end;

function TMenuWithSentTo.GetCommandString(idCmd: UINT_PTR; uFlags: UINT; pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult;
begin
  if IsMyCommandID(idCmd) then
    begin
      // Process your command here
      Result := S_OK;
    end
  else
    FSendToMenu.GetCommandString(idCmd);
end;

function TMenuWithSentTo.HandleMenuMsg(uMsg: UINT; WParam: WPARAM; LParam: LPARAM): HResult;
var
  SendToMenu2: IContextMenu2;
begin
  if IsMyMessage(uMsg, WParam, LParam) then
    begin
      // Process your command here
      Result := S_OK;
    end
  else
    begin
      FSendToMenu.QueryInterface(IContextMenu2, SendToMenu2);
      Result := SendToMenu2.HandleMenuMsg(uMsg, WParam, LParam);
    end;
end;

function TMenuWithSentTo.HandleMenuMsg2(uMsg: UINT; wParam: WPARAM; lParam: LPARAM; var lpResult: LRESULT): HResult;
var
  SendToMenu3: IContextMenu3;
begin
  if IsMyMessage(uMsg, WParam, LParam) then
    begin
      // Process your command here
      Result := S_OK;
    end
  else
    begin
      FSendToMenu.QueryInterface(IContextMenu3, SendToMenu3);
      Result := SendToMenu3.HandleMenuMsg(uMsg, WParam, LParam);
    end;
end;

但你应该准备好一些SendTo命令会被隐藏,有些命令无法正常工作,因为有些命令会请求真实文件,但你只有虚拟文件。

正常发送至菜单:

enter image description here

发送到NSE中的菜单:

enter image description here

答案 1 :(得分:0)

简单的方法是向SendTo文件夹添加快捷方式。要找到它,只需将%APPDATA%\Microsoft\Windows\SendTo粘贴到资源管理器窗口中即可。

仅当您有一个以文件名作为参数的命令行程序时,此方法才有效。如果这不是您所需要的,请编辑您的问题,并详细说明如何访问您的扩展程序代码。另外,如果这是C#,请将其标记。


可以在HKEY_CLASSES_ROOT \ AllFilesystemObjects \ shellex \ ContextMenuHandlers中找到SendTo的注册表项。至少从Vista到Windows 8的价值是{7BA4C740-9E81-11CF-99D3-00AA004AE837}。您可以为此密钥编写shell扩展。我过去做过这个,但是没有任何有用的源代码。文档在这里:http://msdn.microsoft.com/en-us/library/windows/desktop/cc144067%28v=vs.85%29.aspx