It is currently Sat May 25, 2019 8:35 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 10 posts ] 
Author Message
 Post subject: Add the Shell's context menu to your program
PostPosted: Tue Apr 28, 2009 5:10 pm 
Offline
PureBasic Team
PureBasic Team
User avatar

Joined: Fri Apr 25, 2003 5:21 pm
Posts: 5774
Location: Germany
Here is some code to demonstrate that. I hope the comments are sufficient. Note that this only works on Win2k and newer. Tested also with 64bit.

Code:
Structure CMINVOKECOMMANDINFOEX
  cbSize.l
  fMask.l
  hwnd.i
  lpVerb.i
  lpParameters.i
  lpDirectory.i
  nShow.l
  dwHotKey.l
  hIcon.i
  lpTitle.i
  lpVerbW.i
  lpParametersW.i
  lpDirectoryW.i
  lpTitleW.i
  ptInvoke.POINT
EndStructure

Structure QCMINFO
  hmenu.i
  indexMenu.l
  idCmdFirst.l
  idCmdLast.l
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    _alignment.l
  CompilerEndIf
 *pldMap.l
EndStructure

#GCS_VERBA = 0
#CMF_NORMAL = 0
#CMF_CANRENAME = 16

#DFM_MERGECONTEXTMENU   = 1
#DFM_INVOKECOMMAND      = 2
#DFM_GETDEFSTATICID     = 14
#DFM_CMD_PROPERTIES     = -5

; Win2k and newer only!
;
Prototype CDefFolderMenu_Create2(a, b, c, d, e, f, g, h, i)
Global CDefFolderMenu_Create2.CDefFolderMenu_Create2

#Window = 0
#Menu   = 0
#ExplorerList = 0

; These can be changed to limit the range of menu IDs that the context menu will use
; to avoid conflicts with other menus in the program
;
#FirstShellMenuItem = 0
#LastShellMenuItem  = 9999

Global CustomMenuEntry

; Callback function for the CDefFolderMenu_Create2() call
;
Procedure Callback(*psf.IShellFolder, hwnd, pdtobj.IDataObject, uMsg, wParam, lParam)
  Select uMsg
     
    Case #DFM_MERGECONTEXTMENU
       
      ; Here custom entries can be added to the created menu
      ;
      *qcminfo.QCMINFO = lParam
      If *qcminfo\idCmdLast > *qcminfo\idCmdFirst     
       
        If InsertMenu_(*qcminfo\hmenu, *qcminfo\indexMenu, #MF_BYPOSITION|#MF_STRING, *qcminfo\idCmdFirst, @"--- Custom Menu Entry ---")
          ; Save the ID and tell the caller that one entry was added
          ;
          CustomMenuEntry = *qcminfo\idCmdFirst
          *qcminfo\idCmdFirst + 1
        EndIf
       
      EndIf
      ProcedureReturn #S_OK
   
    Case #DFM_INVOKECOMMAND
   
      ; Here the execution of the commands can be overwritten.
      ; return #S_FALSE to get the default behavior
      ProcedureReturn #S_FALSE
   
    Case #DFM_GETDEFSTATICID
   
      ; return #S_FALSE to get the default handling
      ProcedureReturn #S_FALSE
   
    Default
      ProcedureReturn #E_NOTIMPL
     
  EndSelect
EndProcedure

CoInitialize_(0)

If OpenLibrary(0, "shell32.dll")
  CDefFolderMenu_Create2 = GetFunction(0, "CDefFolderMenu_Create2")
 
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    ; Win2k only exports this by ordinal, newer versions export by name
    If CDefFolderMenu_Create2 = 0
      CDefFolderMenu_Create2 = GetFunctionEntry(0, 701)
    EndIf
  CompilerEndIf
 
  If CDefFolderMenu_Create2 = 0
    Debug "Error, cannot find CDefFolderMenu_Create2()"
    End
  EndIf
EndIf

ShellMenu.IContextMenu = 0

If OpenWindow(#Window, 0, 0, 500, 500, "ContextMenu test", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  ExplorerListGadget(#ExplorerList, 10, 10, 480, 480, "C:\", #PB_Explorer_MultiSelect|#PB_Explorer_FullRowSelect|#PB_Explorer_AlwaysShowSelection)

  Repeat
    Event = WaitWindowEvent()
   
    ; Right-click event in the gadget
    ;
    If Event = #PB_Event_Gadget And EventGadget() = #ExplorerList And EventType() = #PB_EventType_RightClick
   
      ; Get the IDL and IShellFolder for the current directory in the gadget
      ;
      If SHGetDesktopFolder_(@Desktop.IShellFolder) = #S_OK
        Debug "got desktop folder"
       
        ParentFolder$ = GetGadgetText(#ExplorerList)               
        If Desktop\ParseDisplayName(WindowID(#Window), #Null, ParentFolder$, #Null, @*ParentIDL, #Null) = #S_OK
          Debug "got parent folder idl"
         
          If Desktop\BindToObject(*ParentIDL, #Null, ?IID_IShellFolder, @Parent.IShellFolder) = #S_OK
            Debug "got parent folder object"
           
           
            ; Get the IDLs for all selected items
            ;
            TotalCount = CountGadgetItems(#ExplorerList)
            ItemCount  = 0
            For i = 0 To TotalCount-1
              If GetGadgetItemState(#ExplorerList, i) & #PB_Explorer_Selected And GetGadgetItemText(#ExplorerList, i, 0) <> ".."
                ItemCount + 1
              EndIf
            Next i
           
            If ItemCount > 0
                         
              Dim *FileIDL(ItemCount-1)
             
              ParsedCount = 0
              For i = 0 To TotalCount-1
                If GetGadgetItemState(#ExplorerList, i) & #PB_Explorer_Selected And GetGadgetItemText(#ExplorerList, i, 0) <> ".."
                  ItemName$ = GetGadgetItemText(#ExplorerList, i, 0)
                 
                  If Parent\ParseDisplayName(WindowID(#Window), #Null, ItemName$, #Null, @*FileIDL(ParsedCount), #Null) = #S_OK
                    ParsedCount + 1
                  EndIf                 
                EndIf
              Next i   
             
              ; Only go to the menu if parsing all items worked correctly
              ;
              If ParsedCount = ItemCount
                Debug "got item idl's"       
               
                ; Free the old menu object
                If ShellMenu
                  ShellMenu\Release()
                  ShellMenu = 0
                EndIf
               
                ; Open the registry keys for shell extensions
                ;
                KeyCount = 1
                Dim KeyStrings.s(KeyCount)
                Dim hKey(KeyCount)
               
                KeyStrings(0) = "*"
;                 KeyStrings(1) = ".txt"
;                 KeyStrings(2) = "txtfile"
               
                KeysOpen = 0
                For i = 0 To KeyCount-1
                  If RegCreateKeyEx_(#HKEY_CLASSES_ROOT, @KeyStrings(i), 0, #Null, 0, #KEY_READ, #Null, @hKey(KeysOpen), #Null) = #ERROR_SUCCESS
                    KeysOpen + 1
                  EndIf
                Next i
               
                ; Create the menu object for our items with the above callback
                ;
                If CDefFolderMenu_Create2(*ParentIDL, WindowID(#Window), ParsedCount, @*FileIDL(), Parent, @Callback(), KeysOpen, @hKey(), @ShellMenu.IContextMenu) = #S_OK
                  Debug "got menu"
                 
                  ; Create a PB popupmenu to put the menu items in
                  ;
                  If CreatePopupMenu(#Menu)
                    Debug "got pb menu"
                   
                    ; Add the Shell menu to our popup menu
                    ; You can specify the range of menu item ids to use here (to not conflict with others from your program)
                    ;                       
                    If ShellMenu\QueryContextMenu(MenuID(#Menu), 0, #FirstShellMenuItem, #LastShellMenuItem, #CMF_NORMAL|#CMF_CANRENAME) >= 0
                      Debug "menu items added"
                   
                      ; Finally display the popup menu
                      ;
                      DisplayPopupMenu(#Menu, WindowID(#Window))
                    EndIf
                  EndIf
                EndIf   
               
                For i = 0 To KeysOpen-1
                  RegCloseKey_(hkey(i))
                Next i
             
              Else
                Debug "error in parsing a selected item"
               
              EndIf
             
              ; Free the item IDLs (as far as they were parsed)
              ;
              For i = 0 To ParsedCount-1
                CoTaskMemFree_(*FileIDL(i))
              Next i
 
            EndIf
           
            Parent\Release()
          EndIf
         
          CoTaskMemFree_(*ParentIDL)
        EndIf
     
        Desktop\Release()
      EndIf
       
   
    ; A menu event from the contextmenu range
    ;
    ElseIf Event = #PB_Event_Menu And ShellMenu And EventMenu() >= #FirstShellMenuItem And EventMenu() <= #LastShellMenuItem
   
      If EventMenu() = CustomMenuEntry
        ; Its our custom menu item
        Debug "--- custom menu item selected ---"
     
      Else
        ; its one of the shell items
       
        Debug "handling event: " + Str(EventMenu())

        Command$ = Space(1000)
        If ShellMenu\GetCommandString(EventMenu(), #GCS_VERBA, #Null, @Command$, 1000) = #S_OK
          Debug "Commmand: " + Command$     
         
          ; Some of these commands can be directly passed to ShellExecute_() for example         
        EndIf
     
        ; Let the menu object execute this command
        ;
        info.CMINVOKECOMMANDINFOEX\cbSize = SizeOf(CMINVOKECOMMANDINFOEX)
        info\fMask  = 0
        info\hwnd   = WindowID(#Window)
        info\lpVerb = EventMenu()
        info\nShow  = #SW_SHOWNORMAL
       
        err = ShellMenu\InvokeCommand(@info)
        If err = #S_OK
          Debug "command executed"
        Else
          Debug "command could not be executed. error = "+Str(err)
        EndIf
      EndIf
   
    EndIf   
     
   
  Until Event = #PB_Event_CloseWindow
EndIf

CoUninitialize_()

End


DataSection

  IID_IShellFolder: ; {000214E6-0000-0000-C000-000000000046}
    Data.l $000214E6
    Data.w $0000, $0000
    Data.b $C0, $00, $00, $00, $00, $00, $00, $46

EndDataSection

_________________
quidquid Latine dictum sit altum videtur


Last edited by freak on Tue Apr 28, 2009 6:07 pm, edited 2 times in total.

Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Apr 28, 2009 5:16 pm 
Offline
Addict
Addict

Joined: Mon Apr 28, 2003 2:22 pm
Posts: 942
Location: Europe
Nice code. Thanks for posting!

Unfortunately it crashes on my vista 64 bit system if I right click arround to test. The debugger does not show the line how the crash appaers couse the application is closed by windows together with the debugger. :-(

EDIT: Crash occures if the PopUp windows is opened more then 3 times. (crashed on 4th click)

_________________
Tranquil


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Apr 28, 2009 5:25 pm 
Offline
PureBasic Team
PureBasic Team
User avatar

Joined: Fri Apr 25, 2003 5:21 pm
Posts: 5774
Location: Germany
It was a wrong prototype definition (the docs where a bit fuzzy there). Please try it again.

Thanks for pointing this out.

_________________
quidquid Latine dictum sit altum videtur


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Apr 28, 2009 5:39 pm 
Offline
Always Here
Always Here
User avatar

Joined: Thu Jun 24, 2004 2:44 pm
Posts: 5754
Location: Berlin - Germany
Is there a way to fill or delete open with and sendto?

_________________
PureBasic 5.70 | SpiderBasic 2.21 | Windows 10 Pro (x64) | Linux Mint 19.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Apr 28, 2009 6:15 pm 
Offline
PureBasic Team
PureBasic Team
User avatar

Joined: Fri Apr 25, 2003 5:21 pm
Posts: 5774
Location: Germany
Apparently this is controlled by the registry key parameter that i left empty so far. I updated the code above to include that (see the part right before the CDefFolderMenu_Create2 call). I do not fully understand how this works though and i am out of time to look further at the moment.

According to this, you have to pass in the registry keys for the selected filetypes.
http://www.zabkat.com/blog/08Jul07.htm

Passing in some keys there modifies the behavior of the menu, but i haven't figured out how to get the SendTo to work for example (it just disappears). Just play around with different registry keys, maybe you can make some sense of it.

Btw, there is also SHCreateDefaultContextMenu() (vista only) that seems to do the same thing. Its maybe worth a look.

_________________
quidquid Latine dictum sit altum videtur


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Apr 28, 2009 6:25 pm 
Offline
Always Here
Always Here
User avatar

Joined: Thu Jun 24, 2004 2:44 pm
Posts: 5754
Location: Berlin - Germany
really hard stuff :mrgreen:

thanks again, i will look, but i think this is a class to high for me

greetings
Thomas

_________________
PureBasic 5.70 | SpiderBasic 2.21 | Windows 10 Pro (x64) | Linux Mint 19.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Apr 28, 2009 6:40 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Wed Oct 29, 2003 4:35 pm
Posts: 10517
Location: Beyond the pale...
Works well here on Vista - thanks. 8)

_________________
I may look like a mule, but I'm not a complete ass.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Apr 28, 2009 7:58 pm 
Offline
Addict
Addict

Joined: Wed Aug 24, 2005 8:39 am
Posts: 2736
Location: Southwest OH - USA
This is really great :)

Solves an outstanding user request I had no clue on.

Thanks freak.

cheers


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Tue Apr 28, 2009 8:21 pm 
Offline
Addict
Addict
User avatar

Joined: Fri Jul 21, 2006 4:41 am
Posts: 2336
Location: Berlin, Germany
I like! :o

_________________
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?


Top
 Profile  
Reply with quote  
 Post subject: Re: Add the Shell's context menu to your program
PostPosted: Sun Feb 14, 2010 3:19 pm 
Offline
Enthusiast
Enthusiast

Joined: Tue Oct 31, 2006 4:34 am
Posts: 527
Thank's freak
Thank you very much for providing this code so I can see how this is done.
I've been trying to figure how to do this for awhile, until I finally ended up posting this question in
another topic thread under Coding Questions. Thanks to srod, he pointed me here.
You example really works great. But what I really appreciate is knowing how to do it now.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 10 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 5 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye