TrackPopupMenu右键弹出菜单栏文本信息不显示

时刻准备中 2013-09-16 07:04:28
大家好:
本人在做一个类似windows资源管理器的界面程序时遇到了一个CListCtrl响应右键,TrackPopupMenu弹出栏中的文本信息无法显示的问题,请大家帮忙看看代码(截取了相关的部分,有点儿长,关注红色部分),哪儿处理有问题?谢谢
效果图如下:


部分代码:
//ShellContextMenu.h
class CShellContextMenu
{
public:
void SetObjects (CStringArray &strArray);
UINT ShowContextMenu (CWnd* pWnd, CPoint pt);
CShellContextMenu();
virtual ~CShellContextMenu();

private:
int nItems;
BOOL bDelete;
CMenu * m_Menu;
IShellFolder * m_psfFolder;
LPITEMIDLIST * m_pidlArray;

BOOL GetContextMenu (void ** ppContextMenu, int & iMenuType);
HRESULT SHBindToParentEx (LPCITEMIDLIST pidl, REFIID riid, VOID **ppv, LPCITEMIDLIST *ppidlLast);
static LRESULT CALLBACK HookWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};
//ShellContextMenu.cpp
#include "stdafx.h"
#include "ShellContextMenu.h"
#include <malloc.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#define MIN_ID 1
#define MAX_ID 10000

IContextMenu2 * g_IContext2 = NULL;
IContextMenu3 * g_IContext3 = NULL;

BOOL CShellContextMenu::GetContextMenu (void ** ppContextMenu, int & iMenuType)
{
*ppContextMenu = NULL;
LPCONTEXTMENU icm1 = NULL;

// first we retrieve the normal IContextMenu interface (every object should have it)
m_psfFolder->GetUIObjectOf (NULL, nItems, (LPCITEMIDLIST *) m_pidlArray, IID_IContextMenu, NULL, (void**) &icm1);

if (icm1)
{ // since we got an IContextMenu interface we can now obtain the higher version interfaces via that
if (icm1->QueryInterface (IID_IContextMenu3, ppContextMenu) == NOERROR)
iMenuType = 3;
else if (icm1->QueryInterface (IID_IContextMenu2, ppContextMenu) == NOERROR)
iMenuType = 2;

if (*ppContextMenu)
icm1->Release(); // we can now release version 1 interface, cause we got a higher one
else
{
iMenuType = 1;
*ppContextMenu = icm1; // since no higher versions were found
} // redirect ppContextMenu to version 1 interface
}
else
return (FALSE); // something went wrong

return (TRUE); // success
}

LRESULT CALLBACK CShellContextMenu::HookWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_MENUCHAR: // only supported by IContextMenu3
if (g_IContext3)
{
LRESULT lResult = 0;
g_IContext3->HandleMenuMsg2 (message, wParam, lParam, &lResult);
return (lResult);
}
break;

case WM_DRAWITEM:
case WM_MEASUREITEM:
if (wParam)
break; // if wParam != 0 then the message is not menu-related

case WM_INITMENUPOPUP:
if (g_IContext2)
g_IContext2->HandleMenuMsg (message, wParam, lParam);
else // version 3
g_IContext3->HandleMenuMsg (message, wParam, lParam);
return (message == WM_INITMENUPOPUP ? 0 : TRUE); // inform caller that we handled WM_INITPOPUPMENU by ourself
break;

default:
break;
}

// call original WndProc of window to prevent undefined bevhaviour of window
return ::CallWindowProc ((WNDPROC) GetProp ( hWnd, TEXT ("OldWndProc")), hWnd, message, wParam, lParam);
}

UINT CShellContextMenu::ShowContextMenu(CWnd *pWnd, CPoint pt)
{
int iMenuType = 0; // to know which version of IContextMenu is supported
LPCONTEXTMENU pContextMenu; // common pointer to IContextMenu and higher version interface

if (!GetContextMenu ((void**) &pContextMenu, iMenuType))
return (0); // something went wrong

if (!m_Menu)
{
delete m_Menu;
m_Menu = NULL;
m_Menu = new CMenu;
m_Menu->CreatePopupMenu ();
}

// lets fill the our popupmenu
pContextMenu->QueryContextMenu (m_Menu->m_hMenu, m_Menu->GetMenuItemCount (), MIN_ID, MAX_ID, CMF_NORMAL | CMF_EXPLORE);

// subclass window to handle menurelated messages in CShellContextMenu
WNDPROC OldWndProc;
if (iMenuType > 1) // only subclass if its version 2 or 3
{
OldWndProc = (WNDPROC) SetWindowLong (pWnd->m_hWnd, GWL_WNDPROC, (DWORD) HookWndProc);
if (iMenuType == 2)
g_IContext2 = (LPCONTEXTMENU2) pContextMenu;
else // version 3
g_IContext3 = (LPCONTEXTMENU3) pContextMenu;
}
else
OldWndProc = NULL;

UINT idCommand = m_Menu->TrackPopupMenu (TPM_RETURNCMD | TPM_LEFTALIGN, pt.x, pt.y, pWnd);

if (OldWndProc) // unsubclass
SetWindowLong (pWnd->m_hWnd, GWL_WNDPROC, (DWORD) OldWndProc);

if (idCommand >= MIN_ID && idCommand <= MAX_ID) // see if returned idCommand belongs to shell menu entries
{
InvokeCommand (pContextMenu, idCommand - MIN_ID); // execute related command
// idCommand = 0;
}

pContextMenu->Release();
g_IContext2 = NULL;
g_IContext3 = NULL;

return (idCommand);
}

void CShellContextMenu::SetObjects(CStringArray &strArray)
{
// free all allocated datas
if (m_psfFolder && bDelete)
m_psfFolder->Release ();
m_psfFolder = NULL;
FreePIDLArray (m_pidlArray);
m_pidlArray = NULL;

// get IShellFolder interface of Desktop (root of shell namespace)
IShellFolder * psfDesktop = NULL;
SHGetDesktopFolder (&psfDesktop); // needed to obtain full qualified pidl

// ParseDisplayName creates a PIDL from a file system path relative to the IShellFolder interface
// but since we use the Desktop as our interface and the Desktop is the namespace root
// that means that it's a fully qualified PIDL, which is what we need
LPITEMIDLIST pidl = NULL;

#ifndef _UNICODE
OLECHAR * olePath = NULL;
olePath = (OLECHAR *) calloc (strArray.GetAt (0).GetLength () + 1, sizeof (OLECHAR));
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, strArray.GetAt(0), -1, olePath, strArray.GetAt (0).GetLength () + 1);
psfDesktop->ParseDisplayName (NULL, 0, olePath, NULL, &pidl, NULL);
free (olePath);
#else
psfDesktop->ParseDisplayName (NULL, 0, strArray.GetAt (0).GetBuffer (0), NULL, &pidl, NULL);
#endif

// now we need the parent IShellFolder interface of pidl, and the relative PIDL to that interface
LPITEMIDLIST pidlItem = NULL; // relative pidl
SHBindToParentEx (pidl, IID_IShellFolder, (void **) &m_psfFolder, NULL);
free (pidlItem);
// get interface to IMalloc (need to free the PIDLs allocated by the shell functions)
LPMALLOC lpMalloc = NULL;
SHGetMalloc (&lpMalloc);
lpMalloc->Free (pidl);

// now we have the IShellFolder interface to the parent folder specified in the first element in strArray
// since we assume that all objects are in the same folder (as it's stated in the MSDN)
// we now have the IShellFolder interface to every objects parent folder

IShellFolder * psfFolder = NULL;
nItems = strArray.GetSize ();
for (int i = 0; i < nItems; i++)
{
#ifndef _UNICODE
olePath = (OLECHAR *) calloc (strArray.GetAt (i).GetLength () + 1, sizeof (OLECHAR));
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, strArray.GetAt (i), -1, olePath, strArray.GetAt (i).GetLength () + 1);
psfDesktop->ParseDisplayName (NULL, 0, olePath, NULL, &pidl, NULL);
free (olePath);
#else
psfDesktop->ParseDisplayName (NULL, 0, strArray.GetAt (i).GetBuffer (0), NULL, &pidl, NULL);
#endif
m_pidlArray = (LPITEMIDLIST *) realloc (m_pidlArray, (i + 1) * sizeof (LPITEMIDLIST));
// get relative pidl via SHBindToParent
SHBindToParentEx (pidl, IID_IShellFolder, (void **) &psfFolder, (LPCITEMIDLIST *) &pidlItem);
m_pidlArray[i] = CopyPIDL (pidlItem); // copy relative pidl to pidlArray
free (pidlItem);
lpMalloc->Free (pidl); // free pidl allocated by ParseDisplayName
psfFolder->Release ();
}
lpMalloc->Release ();
psfDesktop->Release ();

bDelete = TRUE; // indicates that m_psfFolder should be deleted by CShellContextMenu
}

HRESULT CShellContextMenu::SHBindToParentEx (LPCITEMIDLIST pidl, REFIID riid, VOID **ppv, LPCITEMIDLIST *ppidlLast)
{
HRESULT hr = 0;
if (!pidl || !ppv)
return E_POINTER;

int nCount = GetPIDLCount (pidl);
if (nCount == 0) // desktop pidl of invalid pidl
return E_POINTER;

IShellFolder * psfDesktop = NULL;
SHGetDesktopFolder (&psfDesktop);
if (nCount == 1) // desktop pidl
{
if ((hr = psfDesktop->QueryInterface(riid, ppv)) == S_OK)
{
if (ppidlLast)
*ppidlLast = CopyPIDL (pidl);
}
psfDesktop->Release ();
return hr;
}

LPBYTE pRel = GetPIDLPos (pidl, nCount - 1);
LPITEMIDLIST pidlParent = NULL;
pidlParent = CopyPIDL (pidl, pRel - (LPBYTE) pidl);
IShellFolder * psfFolder = NULL;

if ((hr = psfDesktop->BindToObject (pidlParent, NULL,IID_IShellFolder, (void**)&psfFolder)) != S_OK)
{
free (pidlParent);
psfDesktop->Release ();
return hr;
}
if ((hr = psfFolder->QueryInterface (riid, ppv)) == S_OK)
{
if (ppidlLast)
*ppidlLast = CopyPIDL ((LPCITEMIDLIST) pRel);
}
free (pidlParent);
psfFolder->Release ();
psfDesktop->Release ();
return hr;
}
//右键响应
void CFtpView::OnNMRClickLocalList(NMHDR *pNMHDR, LRESULT *pResult)
{
NMITEMACTIVATE * nmia = (NMITEMACTIVATE *) pNMHDR;

POSITION pos = m_local_list_file.GetFirstSelectedItemPosition();
if ( pos==NULL )
{
return;
}

char * szPath = new char[MAX_PATH];
CStringArray sPathArray;
while (pos)
{
ZeroMemory(szPath,MAX_PATH);
int nItem = m_local_list_file.GetNextSelectedItem(pos);
LPLISTPARAM pliParam =(LPLISTPARAM) m_local_list_file.GetItemData(nItem);
SHGetPathFromIDList(pliParam->lpidl,szPath);
sPathArray.Add(szPath);
}
delete szPath;

CShellContextMenu scm;
scm.SetObjects(sPathArray);
CPoint point (nmia->ptAction);
m_local_list_file.ClientToScreen(&point);
UINT idCommand = scm.ShowContextMenu(this, point)
;

switch( idCommand )
{
case 18: //chose delete
{
SendMessage(WM_REFRESHTREE,(WPARAM)m_hSelItem,0);
SendMessage(WM_REFRESHFILES,0,0);
break;
}
case 27: //chose paste
{
break;
}
}

*pResult = 0;
}
...全文
309 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
凌乱哥 2013-10-16
  • 打赏
  • 举报
回复
貌似没看到你代码中用到该菜单的资源ID,比如IDR_MENU1
向立天 2013-10-16
  • 打赏
  • 举报
回复
您好 我是本版版主 此帖已多日无人关注 请您及时结帖 如您认为问题没有解决可按无满意结帖处理 另外本版设置了疑难问题汇总帖 并已在版面置顶 相关规定其帖子中有说明 您可以根据规定提交您帖子的链接 如您目前不想结帖只需回帖说明 我们会删除此结帖通知 见此回复三日内无回应 我们将强制结帖 相关规定详见界面界面版关于版主结帖工作的具体办法
时刻准备中 2013-09-16
  • 打赏
  • 举报
回复

    int cntMain=m_Menu->GetMenuItemCount();
    for(int i=0;i<cntMain;i++)
    {
        CString menuName;
        m_Menu->GetMenuString(i,menuName,MF_BYPOSITION);

        CMenu *cMenuSub = NULL;
        int cntSub = 0;
        m_Menu->GetSubMenu(i);
        if (cMenuSub != NULL)
        {
            cntSub = cMenuSub->GetMenuItemCount();
            for(int j=0;j<cntSub;j++)
            {
                cMenuSub->GetMenuString(j,menuName,MF_BYPOSITION);
            }
        }
    }
遍历menu,里面的值也是正常的,跟系统右键弹出的内容是一样的;只是遇到子菜单的时候总会获取到一个空的
时刻准备中 2013-09-16
  • 打赏
  • 举报
回复
引用 2 楼 modyaj 的回复:
是不是 显示了后要书刷新界面啊 哎呀 看得眼花 插入C/C++代码吧
弹出菜单需要刷新吗?
时刻准备中 2013-09-16
  • 打赏
  • 举报
回复
引用 1 楼 likun_vc 的回复:
是不是全局变量的关系?方法结束了,变量消失了,用全局或成员变量
CMenu * m_Menu;是成员;TrackPopupMenu会等待选择的,选择一项或是点击空白处不选,才会返回,所以应该不是资源释放的问题
modyaj 2013-09-16
  • 打赏
  • 举报
回复
是不是 显示了后要书刷新界面啊 哎呀 看得眼花 插入C/C++代码吧
圆道 2013-09-16
  • 打赏
  • 举报
回复
是不是全局变量的关系?方法结束了,变量消失了,用全局或成员变量

15,979

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧