如果我的程序的列表控件中显示了某个文件夹中的内容,然后希望实现如上的功能,也就是用户右键点击某一项时弹出系统的菜单,该如何做呢?谢谢了先,呵呵。

解决方案 »

  1.   

    我只找到一个vb的程序,没时间调试成vc的(vb也不熟),那位高手有时间吗?
      

  2.   

    http://www.blogcn.com/user3/jiangsheng/index.html?id=1508188
      

  3.   

    做出来了,给大伙发出来说一下阿,我大概看了一下,主要就是调ShellContextMenu函数,他那个例子里是通过右键判断在目录或者文件上而弹出对应的菜单,还可以自己加菜单项。Private Sub Form_Load()
    '  File1.MultiSelect = 2
      Move (Screen.Width - Width) * 0.5, (Screen.Height - Height) * 0.5
    End SubPrivate Sub Drive1_Change()
      On Error GoTo Out   ' covers invalid path selection
      Dir1.Path = Drive1.Drive
    Out:
    End SubPrivate Sub Dir1_Change()
      On Error GoTo Out   ' covers invalid path selection
      File1 = Dir1.Path
    Out:
    End SubPrivate Sub Dir1_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
      If (Button = vbRightButton) Then
        Call ShellContextMenu(Dir1, x, y, Shift)
      End If
    End Sub' Selects all FileListBox items on a Ctrl+A keypress.Private Sub File1_KeyDown(KeyCode As Integer, Shift As Integer)
      If (KeyCode = vbKeyA) And (Shift = vbCtrlMask) Then
        Call SendMessage(File1.hWnd, LB_SETSEL, CTrue, ByVal -1)
      End If
    End SubPrivate Sub File1_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
      If (Button = vbRightButton) Then
        Call ShellContextMenu(File1, x, y, Shift)
      End If
    End SubPrivate Sub ShellContextMenu(objLB As Control, _
                                                     x As Single, _
                                                     y As Single, _
                                                     Shift As Integer)
      
      Dim pt As POINTAPI               ' screen location of the cursor
      Dim iItem As Integer                ' listbox index of the selected item (item under the cursor)
      Dim cItems As Integer             ' count of selected items
      Dim i As Integer                       ' counter
      Dim asPaths() As String           ' array of selected items' paths (zero based)
      Dim apidlFQs() As Long           ' array of selected items' fully qualified pidls (zero based)
      Dim isfParent As IShellFolder   ' selected items' parent shell folder
      Dim apidlRels() As Long           ' array of selected items' relative pidls (zero based)
      
      ' ==================================================
      ' Get the listbox item under the cursor
      
      ' Convert the listbox's client twip coords to screen pixel coords.
      pt.x = x \ Screen.TwipsPerPixelX
      pt.y = y \ Screen.TwipsPerPixelY
      Call ClientToScreen(objLB.hWnd, pt)  ' Get the zero-based index of the item under the cursor.
      ' If none exists, bail...
      iItem = LBItemFromPt(objLB.hWnd, pt.x, pt.y, False)
      If (iItem = LB_ERR) Then Exit Sub
        
      ' ==================================================
      ' Set listbox focus and selection
      
      objLB.SetFocus
      
      ' If neither the Control and/or Shift key are pressed...
      If (Shift And (vbCtrlMask Or vbShiftMask)) = False Then
        
        ' If Dir1 has the focus...
        If (TypeOf objLB Is DirListBox) Then
          ' Select the item under the cursor. The DirListBox
          ' doesn't have a Selected property, so we'll get forceful...
          Call SendMessage(Dir1.hWnd, LB_SETCURSEL, iItem, 0)
        
        Else
          ' File1 has the focus, duplicate Explorer listview selection functionality.
          
          ' If the right clicked item isn't selected....
          If (File1.Selected(iItem) = False) Then
            ' Deselect all of the items and select the right clicked item.
            Call SendMessage(File1.hWnd, LB_SETSEL, CFalse, ByVal -1)
            File1.Selected(iItem) = True
          Else
          ' The right clciked item is selected, give it the selection rectangle
          ' (or caret, does not deselect any other currently selected items).
          ' File1.Selected doesn't set the caret if the item is already selected.
            Call SendMessage(File1.hWnd, LB_SETCARETINDEX, iItem, ByVal 0&)
          End If
        
        End If   '  (TypeOf objLB Is DirListBox)
      End If   ' (Shift And (vbCtrlMask Or vbShiftMask)) = False
      
      ' ==================================================
      ' Load the path(s) of the selected listbox item(s) into the array.
      
      If (TypeOf objLB Is DirListBox) Then
        ' Only one directory can be selected in the DirLB
        cItems = 1
        ReDim asPaths(0)
        asPaths(0) = GetDirLBItemPath(Dir1, iItem)
      
      Else
        ' Put the focused (and selected) files's relative pidl in the
        ' first element of the array. This will be the file whose context
        ' menu will be shown if multiple files are selected.
        cItems = 1
        ReDim asPaths(0)
        asPaths(0) = GetFileLBItemPath(File1, iItem)
        
        ' Fill the array with the relative pidls of the rest of any selected
        ' files(s), making sure that we don't add the focused file again.
        For i = 0 To File1.ListCount - 1
          If (File1.Selected(i)) And (i <> iItem) Then
            cItems = cItems + 1
            ReDim Preserve asPaths(cItems - 1)
            asPaths(cItems - 1) = GetFileLBItemPath(File1, i)
          End If
        Next
      
      End If   ' (TypeOf objLB Is DirListBox)
      
      

  4.   

    ' ==================================================
      ' Finally, get the IShellFolder of the selected directory, load the relative
      ' pidl(s) of the selected items into the array, and show the menu.
      ' This part won't be elaborated upon, as it is extensively involved.
      ' For more info on IShellFolder, pidls and the shell's context menu, see:
      ' http://msdn.microsoft.com/developer/sdk/inetsdk/help/itt/Shell/NameSpace.htm
      
      If Len(asPaths(0)) Then
        
        ' Get a copy of each selected item's fully qualified pidl from it's path.
        For i = 0 To cItems - 1
          ReDim Preserve apidlFQs(i)
          apidlFQs(i) = GetPIDLFromPath(hWnd, asPaths(i))
        Next
        
        If apidlFQs(0) Then
        
          ' Get the selected item's parent IShellFolder.
          Set isfParent = GetParentIShellFolder(apidlFQs(0))
          If (isfParent Is Nothing) = False Then
            
            ' Get a copy of each selected item's relative pidl (the last item ID)
            ' from each respective item's fully qualified pidl.
            For i = 0 To cItems - 1
              ReDim Preserve apidlRels(i)
              apidlRels(i) = GetItemID(apidlFQs(i), GIID_LAST)
            Next
            
            If apidlRels(0) Then
              ' Subclass the Form so we catch the menu's ownerdraw messages.
              Call SubClass(hWnd, AddressOf WndProc)
              ' Show the shell context menu for the selected items. If a
              ' menu command was executed, refresh the two listboxes.
              If ShowShellContextMenu(hWnd, isfParent, cItems, apidlRels(0), pt, chkPrompt) Then
                Dir1.Refresh
                Call RefreshListBox(File1)
              End If
              ' Finally, unsubclass the form.
              Call UnSubClass(hWnd)
            End If   ' apidlRels(0)
            
            ' Free each item's relative pidl.
            For i = 0 To cItems - 1
              Call MemAllocator.Free(ByVal apidlRels(i))
            Next
            
          End If   ' (isfParent Is Nothing) = False      ' Free each item's fully qualified pidl.
          For i = 0 To cItems - 1
            Call MemAllocator.Free(ByVal apidlFQs(i))
          Next
          
        End If   ' apidlFQs(0)
      End If   ' Len(asPaths(0))
      
    End SubPrivate Function GetFileLBItemPath(objFLB As FileListBox, iItem As Integer) As String
      Dim sPath As String
      
      sPath = objFLB.Path
      If Right(sPath, 1) <> "\" Then sPath = sPath & "\"
      GetFileLBItemPath = sPath & objFLB.List(iItem)End Function' Returns the DirListBox Path from the specified listbox item index.
    ' It's a little extra work getting the path of the selected DirListBox item...Private Function GetDirLBItemPath(objDLB As DirListBox, iItem As Integer) As String
      Dim nItems As Integer
      
      ' Get the count of items in the DirLB
      nItems = SendMessage(objDLB.hWnd, LB_GETCOUNT, 0, 0)
      If (nItems > -1) Then   ' LB_ERR
            
        ' Subtract the actual number of LB items from the sum of:
        '   the DirLB's ListCount and
        '   the currently selected directory's real LB index value
        ' (nItems is a value of 1 greater than the last item's real LB index value)
        GetDirLBItemPath = objDLB.List((objDLB.ListCount + iItem) - nItems)'Debug.Print "iItem: " & iItem & ", LiistIndex: " & (objDLB.ListCount + iItem) - nItems  End IfEnd FunctionPrivate Sub RefreshListBox(objLB As Control)
      Dim iFocusedItem As Integer
      Dim i As Integer
      Dim cItems As Integer
      Dim aiSelitems() As Integer
      
      ' Cache the focused item, if any.
      iFocusedItem = objLB.ListIndex
      
      ' Cache any selected items
      For i = 0 To objLB.ListCount - 1
        If objLB.Selected(i) Then
          cItems = cItems + 1
          ReDim Preserve aiSelitems(cItems - 1)
          aiSelitems(cItems - 1) = i
        End If
      Next
      
      ' Refresh the listbox, sets ListIndex = 0, and removes all selction.
      objLB.Refresh  ' Restore focus and selection to the cached items.
    '  objLB.ListIndex = iFocusedItem   ' this errs... (?)
      Call SendMessage(objLB.hWnd, LB_SETCARETINDEX, iFocusedItem, ByVal 0&)
      For i = 0 To cItems - 1
    '    objLB.Selected(aiSelitems(i)) = True   ' may err...
        Call SendMessage(objLB.hWnd, LB_SETSEL, CTrue, ByVal aiSelitems(i))
      Next
      
    End Sub
      

  5.   

    to jiangsheng(蒋晟.MSMVP2004Jan)
        感谢你的提示
    to 蔬菜
        感谢你的关心.我也不太懂vb,所以先把你的代码copy了一份,回去再好好研究.如果不行的话在来问你们好了,呵呵
      

  6.   

    好久没来了,这个问题今天有了点进展。
         在网上找到一点资料,整理为一段代码如下:// Helpers
    enum
    {
    MI_BEGIN = 1,
    MI_END = 0x7FFF,
    MI_INVALID
    };
    inline 
    USHORT& SHIdSize(LPCITEMIDLIST pidl, int offset)
    {
        return ((LPITEMIDLIST)(((char*)pidl)+offset))->mkid.cb;
    }inline 
    USHORT& SHIdSize(LPCITEMIDLIST pidl)
    {
        return ((LPITEMIDLIST)pidl)->mkid.cb;
    }inline
    LPITEMIDLIST NextPidl(LPCITEMIDLIST pidl, int offset)
    {
        return (LPITEMIDLIST)(((char*)pidl)+offset);
    }inline 
    LPITEMIDLIST NextPidl(LPCITEMIDLIST pidl)
    {
        return (LPITEMIDLIST)(((char*)pidl)+SHIdSize(pidl));
    }void SplitPidl(IMalloc* pmalloc, LPCITEMIDLIST pidl, LPITEMIDLIST& pidlFolder, LPITEMIDLIST& pidlObject)
    {
        int n1;
        int n;
        LPITEMIDLIST p;
        for ( n1 = 0, n = 0, p = (LPITEMIDLIST)pidl; SHIdSize(p); n1 = n, n += SHIdSize(p), p = NextPidl(p) )
            ;    if ( n1 == 0 )
            pidlFolder = NULL;
        else if ( pidlFolder = (LPITEMIDLIST)(pmalloc->Alloc(n1+2)) ) {
            CopyMemory(pidlFolder, pidl, n1);
            SHIdSize(pidlFolder, n1) = 0;
        }    int m = n-n1;
        if ( m == 0 )
            pidlObject = NULL;
        else if ( pidlObject = (LPITEMIDLIST)(pmalloc->Alloc(m+2)) ) {
            CopyMemory(pidlObject, NextPidl(pidl, n1), m);
            SHIdSize(pidlObject, m) = 0;
        }
    }// Global Var for owner-draw
    IContextMenu2* pMenu2 = NULL;void CContextMenuDlg::OnRclickList(NMHDR* pNMHDR, LRESULT* pResult) 
    {
    // TODO: Add your control notification handler code here // 弹出“C:\Downloads”文件夹的浏览器菜单
    CString sFile = _T("C:\\Downloads");
    WCHAR cBuffer[1024] = {0};
    ASSERT(MultiByteToWideChar(CP_ACP,
    0, 
    (LPCSTR)(LPCTSTR) sFile,
    sFile .GetLength() + 1,
    cBuffer, 
    sFile .GetLength() + 1)); HRESULT hRes;
    LPMALLOC pMalloc = NULL;
    hRes = SHGetMalloc(&pMalloc);
    ASSERT(SUCCEEDED(hRes)); // Get DesktopShellFolder Interface
    IShellFolder* pDesktopShellFolder = NULL;
    hRes = SHGetDesktopFolder(&pDesktopShellFolder);
    ASSERT(SUCCEEDED(hRes)); // Get Full ItemList List of the File&Folder
    ITEMIDLIST * pFullItemIDList = NULL;
    hRes = pDesktopShellFolder ->ParseDisplayName(GetSafeHwnd(), 
    NULL,
    cBuffer,
    NULL,
    &pFullItemIDList,
    NULL);
    ASSERT(SUCCEEDED(hRes));
    // Split Full to Parent Folder and Object Item
    LPITEMIDLIST pidlFolder = NULL;
    LPITEMIDLIST pidlObj = NULL;
    SplitPidl(pMalloc, pFullItemIDList, pidlFolder, pidlObj); // Get Parent ShellFolder 
    IShellFolder* pParentShellFolder = NULL;
    hRes = pDesktopShellFolder ->BindToObject(pidlFolder, NULL, IID_IShellFolder, (void**)&pParentShellFolder);
    ASSERT(SUCCEEDED(hRes)); // Get IContextMenu of the Object
    IContextMenu* pMenu = NULL;
    hRes = pParentShellFolder ->GetUIObjectOf(GetSafeHwnd(), 1, (LPCITEMIDLIST*)&pidlObj, IID_IContextMenu, NULL, (LPVOID*)&pMenu);
    ASSERT(SUCCEEDED(hRes)); // Create a Empty Menu to Fill Context Menus in
    HMENU hMenu = CreatePopupMenu();
    pMenu ->QueryContextMenu(hMenu, 0, MI_BEGIN, MI_END, CMF_NORMAL); // Get IContextMenu2 for forwarding owner-draw menu messages
    if ( pMenu ->QueryInterface(IID_IContextMenu2,  (void**)&pMenu2) != S_OK ) {
    pMenu2 = NULL;
    }
    // Show Menu
    int idCmd;
    CPoint ptCursor;
    GetCursorPos(&ptCursor);
        idCmd = TrackPopupMenuEx(hMenu, TPM_LEFTBUTTON|TPM_RIGHTBUTTON|TPM_RETURNCMD, ptCursor .x, ptCursor .y, GetSafeHwnd(), NULL); // Invoke Cmd
    if ( idCmd >= MI_BEGIN ) {
    CMINVOKECOMMANDINFO cmi;
    cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
    cmi.fMask = 0;
    cmi.hwnd = GetSafeHwnd();
    cmi.lpVerb = MAKEINTRESOURCE(idCmd - MI_BEGIN);
    cmi.lpParameters = NULL;
    cmi.lpDirectory = NULL;
    cmi.nShow = SW_SHOWNORMAL;
    cmi.dwHotKey = 0;
    cmi.hIcon = NULL;
    pMenu->InvokeCommand(&cmi);
    } if ( pMenu2 != NULL ) {
    pMenu2 ->Release();
    pMenu2 = NULL;
    }
    // Release
    pMenu ->Release();
    pParentShellFolder ->Release(); pMalloc ->Free(pidlObj);
    pMalloc ->Free(pidlFolder);
    pMalloc ->Free(pFullItemIDList); pDesktopShellFolder ->Release();
    pMalloc ->Release();
    }
    LRESULT CContextMenuDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
    {
    // TODO: Add your specialized code here and/or call the base class
    switch(message)
    {
             // 不知道这里为什么会发生断言错误?
    case WM_DRAWITEM:
    case WM_INITMENUPOPUP:
             case WM_MEASUREITEM:
    if(pMenu2)
    pMenu2 ->HandleMenuMsg(message, wParam, lParam);
    break;
    }
    return CDialog::WindowProc(message, wParam, lParam);
    }    以上代码只能弹出弹出一级菜单,当鼠标移动到多级菜单(如打开方式,发送到)的时候就会出现断言错误。但是在Win32类型的工程中运行没问题(以上是基于MFC的),不知道为什么。
      

  7.   

    我又在VS2003里面试了一下,发现没有任何问题。狂晕~
    看来只有结贴了。
    忘记了一点:在初始化的时候要用OleInitialize(),不要使用CoInitialize(),否则不能进行复制操作。
        希望有高人能在搞定或者已经知道的时候通知我一下,严重感谢。[email protected]