📄 shellcontextmenu.cpp
字号:
// ShellContextMenu.cpp: implementation of the CShellContextMenu class.
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ShellContextMenu.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
BOOL CShellContextMenu::InvokeCommand(UINT nCmdID)
{
if (!IsValid() || !IsCommandInRange(nCmdID))
return FALSE;
CMINVOKECOMMANDINFO cmi;
ZeroMemory(&cmi, sizeof(CMINVOKECOMMANDINFO));
cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
cmi.hwnd = m_pOwner->GetSafeHwnd();
cmi.lpVerb = MAKEINTRESOURCE(nCmdID - m_nCmdFirstID);
cmi.nShow = SW_SHOWNORMAL;
return (NOERROR == m_pCtxMenu->InvokeCommand(&cmi));
}
BOOL CShellContextMenu::FillMenu(CMenu *pMenu, UINT nStartIndex, UINT nCmdFirstID, UINT nCmdLastID, UINT nFlags)
{
if (!IsValid())
return FALSE;
m_nCmdFirstID = nCmdFirstID;
m_nCmdLastID = nCmdLastID;
UINT nDefID = pMenu->GetDefaultItem(GMDI_USEDISABLED, TRUE);
HRESULT hr = m_pCtxMenu->QueryContextMenu(pMenu->GetSafeHmenu(),
nStartIndex, m_nCmdFirstID, m_nCmdLastID, nFlags);
if (nFlags & CMF_NODEFAULT)
pMenu->SetDefaultItem(nDefID, TRUE);
if (FAILED(hr))
{
m_nCmdLastID = m_nCmdFirstID = 0;
return FALSE;
}
m_nCmdLastID = m_nCmdFirstID + HRESULT_CODE(hr) - 1;
return TRUE;
}
BOOL CShellContextMenu::HandleMenuMessage(UINT message, WPARAM wParam, LPARAM lParam, LRESULT *pLResult)
{
if (!IsValid())
return FALSE;
switch (message)
{
case WM_MENUSELECT:
{
// look for a parent frame, to update its status bar
if (m_pOwner == NULL)
return FALSE;
CFrameWnd* pFrame = m_pOwner->GetTopLevelFrame();
if (pFrame == NULL)
return FALSE;
// provide status bar text
*pLResult = 0;
CString sStatusText;
UINT uItem = (UINT) LOWORD(wParam);
UINT fuFlags = (UINT) HIWORD(wParam);
if (fuFlags == 0xFFFF) // menu was closed
return FALSE;
// get the first item of submenu, if popup item
if (fuFlags & MF_POPUP)
uItem = ::GetMenuItemID(::GetSubMenu((HMENU)lParam, uItem), 0);
// check if it's a valid item
if (!(fuFlags & MF_SEPARATOR)
&& (uItem == 0 || !IsCommandInRange(uItem)))
return FALSE;
// get description
if (!(fuFlags & MF_SEPARATOR))
GetCommandDescription(uItem, sStatusText);
// update status text, even if empty
pFrame->SetMessageText(sStatusText);
return TRUE;
}
break;
case WM_INITMENUPOPUP: // initialize sub-menus
case WM_MEASUREITEM: // handle owner-draw items
case WM_DRAWITEM:
{
// prepare result in case we handle the message
// (if we don't, it will be discarded)
*pLResult = TRUE;
if (message == WM_INITMENUPOPUP)
*pLResult = 0;
// check if it's a menu message
if (message == WM_MEASUREITEM
&& ((LPMEASUREITEMSTRUCT)lParam)->CtlType != ODT_MENU)
return FALSE;
if (message == WM_DRAWITEM
&& ((LPDRAWITEMSTRUCT)lParam)->CtlType != ODT_MENU)
return FALSE;
// these messages need interface version 2
SContextMenu2Ptr pCtxMenu2(m_pCtxMenu);
if (!pCtxMenu2.IsValid())
return FALSE;
return (NOERROR == pCtxMenu2->HandleMenuMsg(message, wParam, lParam));
}
}
return FALSE;
}
BOOL CShellContextMenu::GetCommandDescription(UINT nCmdID, CString& rText)
{
if (!IsValid() || !IsCommandInRange(nCmdID))
return FALSE;
// make a buffer large enough to hold either a Unicode or Ansi
// string and initialize it to zeroes, because some shell
// extensions return success even when they don't copy anything
// to the buffer. Conversion is achieved by a CString object.
WCHAR szHelpText[MAX_PATH];
ZeroMemory(szHelpText, sizeof(WCHAR)*MAX_PATH);
// try with Unicode first (it seems Explorer does so)
HRESULT hr = m_pCtxMenu->GetCommandString(nCmdID - m_nCmdFirstID,
GCS_HELPTEXTW, NULL, (LPSTR)szHelpText, MAX_PATH);
if (*(LPWSTR)szHelpText != 0) // buffer was used
rText = (LPWSTR)szHelpText;
else
{
// otherwise try with Ansi
hr = m_pCtxMenu->GetCommandString(nCmdID - m_nCmdFirstID,
GCS_HELPTEXTA, NULL, (LPSTR)szHelpText, MAX_PATH);
if (*(LPSTR)szHelpText != 0) // buffer was used
rText = (LPSTR)szHelpText;
}
return SUCCEEDED(hr);
}
BOOL CShellContextMenu::Create(IShellFolder *pParentFolder, LPCITEMIDLIST pidl)
{
m_pCtxMenu = SContextMenuPtr(pParentFolder, pidl);
return m_pCtxMenu.IsValid();
}
// a NULL pointer to unsubclass
void CShellContextMenu::SetOwner(CWnd *pOwner)
{
if (m_pOwner != NULL || pOwner == NULL)
UnsubclassOwner();
m_pOwner = pOwner;
if (m_pOwner != NULL)
SubclassOwner();
}
const LPCTSTR SH_CTXMENU_OBJ = _T("ShellContextMenu");
void CShellContextMenu::SubclassOwner()
{
ASSERT(::IsWindow(m_pOwner->GetSafeHwnd()));
m_oldWndProc = (WNDPROC)::GetWindowLong(*m_pOwner, GWL_WNDPROC);
::SetProp(*m_pOwner, SH_CTXMENU_OBJ, (HANDLE)this);
::SetWindowLong(*m_pOwner, GWL_WNDPROC, (LONG)WindowProc);
}
LRESULT CALLBACK CShellContextMenu::WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
CShellContextMenu *pThis =
(CShellContextMenu*)::GetProp(hwnd, SH_CTXMENU_OBJ);
LRESULT lResult;
if (pThis->HandleMenuMessage(message, wParam, lParam, &lResult))
return lResult; // success, no default processing
if (message == WM_DESTROY)
pThis->UnsubclassOwner();
return ::CallWindowProc(pThis->m_oldWndProc, hwnd, message, wParam, lParam);
}
void CShellContextMenu::UnsubclassOwner()
{
ASSERT(::IsWindow(m_pOwner->GetSafeHwnd()));
::RemoveProp(*m_pOwner, SH_CTXMENU_OBJ);
::SetWindowLong(*m_pOwner, GWL_WNDPROC, (LONG)m_oldWndProc);
m_pOwner = NULL;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -