AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Invoking directly ContextMenu of Files and Folders
Goto page Previous  1, 2, 3, 4, 5  Next
 
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
Guest






PostPosted: Tue May 26, 2009 1:34 pm    Post subject: Reply with quote

sorry for a no0b question
I know this script is useful but how exactly do I use it?
I mean I know extract the file and run it,
but it shows the context menu only once, per time I launch the script?

is there any key to launch the context menu?
and how is this better than the regular context menu?

please reply.
Back to top
Eucaly61



Joined: 16 May 2009
Posts: 18
Location: TW

PostPosted: Tue May 26, 2009 1:53 pm    Post subject: Reply with quote

Anonymous wrote:
... how exactly do I use it? ...

Trigger from hotkey, or as a portion of a large script ....
Do not limit your imagination (and also innovation)

Anonymous wrote:
...how is this better than the regular context menu?

the ContextMenu is registed under your AutoHotKey thread, thus you can do some pre-process and/or post-process as you like.

inspired from this, I make a varient that get and invoke menu items without bring up popup menu

I will show that in next post
_________________
Eucaly61's DIY World (Mainly in Chinese)
http://eucaly61.blogspot.com/search/label/AutoHotKey
Back to top
View user's profile Send private message
Eucaly61



Joined: 16 May 2009
Posts: 18
Location: TW

PostPosted: Tue May 26, 2009 2:38 pm    Post subject: Reply with quote

.
below is the demo code to get and invoke ContextMenu (ShellEx) items without bring up popup menu

(dependency: CoHelper.ahk)

Code:
#SingleInstance Force
SetBatchLines,-1

#Include CoHelper.ahk

; FileName : ShellExt-Demo-008.ahk

sIId = {000214E4-0000-0000-C000-000000000046}   ; IContextMenu

;sDll := "igfxpph.dll"
sClsId1 = {3AB1675A-CCFF-11D2-8B20-00A0C93CB1F4} igfx Shellext

;sDll := "wmpshell.dll"
sClsId2 = {F1B9284F-E9DC-4e68-9D7E-42362A59F0FD} add to playlist
; => could see menu, but did not try more details

;sDll := "shdocvw.dll"
sClsId3 = {2559a1f0-21d7-11d4-bdaf-00c04f60b9f0} findfiles

;sDll := "shdocvw.dll"
sClsId4 = {2559a1f1-21d7-11d4-bdaf-00c04f60b9f0} help

;sDll := "SHELL32.dll"
sClsId5 = {D969A300-E7FF-11d0-A93B-00A0C90F2719} New ...
; => not able to explore 2nd level, maybe it's context sensitive

sClsId6 = {7BA4C740-9E81-11CF-99D3-00AA004AE837} send-to
; => not able to explore 2nd level, maybe it's context sensitive

; You could add more CLSID (clsid7, 8, 9 and so on),
   ; only those enclosed by {...} are recognized as CLSID

Gui, Font, S12
Gui, Add, ListBox, w500 r10 vClsIdList
Gui, Add, Button, ,Go

lst=
Loop
{
   if !sClsId%A_index%
      break
   lst .= "|" . sClsId%A_index%
}

GuiControl,, ClsIdList, %lst%
Gui, SHow
return

GuiClose:
   ExitApp

ButtonGo:
   Gui, Submit, noHide
   p1 := RegExMatch(ClsIdList,"(?P<ClsId>\{[^\}]*\})",s)

   if !p1
      return

   Critical
   CoInitialize()

   pcm := CreateObject(sClsId,sIId)
   if !pcm
      return

l := ClsIdList . "`n`n"

   hMenu := DllCall("CreatePopupMenu")
   DllCall(VTable(pcm,3), "Uint", pcm
         ,"Uint", hMenu, "Uint", 0
         , "Uint", 3, "Uint", 0x7FFF, "Uint", 0)   ; QueryContextMenu

l .= "idx / idn / MenuItem / hSubMenu`n"
l .= "========================================`n"

   Loop, % DllCall( "GetMenuItemCount", UInt,hMenu ) {
      idx := A_Index-1
      idn := DllCall( "GetMenuItemID", UInt,hMenu, Int,idx )
      nSize := DllCall( "GetMenuString"
            , UInt,hMenu, Int,idx, Int,0, Int,0, UInt,0x400 ) + 1
      VarSetCapacity( mStr,nSize )
      DllCall( "GetMenuString"
             , UInt,hMenu, Int,idx, Str,mStr, Int,nSize, UInt,0x400 )

      hSub := DllCall("GetSubMenu",Uint,hMenu,int,idx)

l .= idx . " / " . idn . " / " . mstr . " / " . hSub . "`n"

      if hSub
      {
l .= "--------------------`n"
         Loop, % DllCall( "GetMenuItemCount", UInt,hSub ) {
            idx := A_Index-1
            idn := DllCall( "GetMenuItemID", UInt,hSub, Int,idx )
            nSize := DllCall( "GetMenuString"
                  , UInt,hSub, Int,idx, Int,0, Int,0, UInt,0x400 ) + 1
            VarSetCapacity( mStr1,nSize )
            DllCall( "GetMenuString"
                , UInt,hSub, Int,idx, Str,mStr1, Int,nSize, UInt,0x400 )

            hSub1 := DllCall("GetSubMenu",Uint,hSub,int,idx)
      
l .= "[" . idx . "] / " . idn . " / " . mstr . " -- " . mstr1 . " / " . hSub1 . "`n"
         }
l .= "--------------------`n"
      }
   }

msgbox, % l


; implement below to invoke menu command with given idn
if (0) {
   DetectHiddenWindows, On
   Process, Exist
   WinGet, hAHK, ID, ahk_pid %ErrorLevel%

;   idn := .....

   DllCall("GetCursorPos", "int64P", pt)

   NumPut(VarSetCapacity(ici,64,0),ici), NumPut(0x4000|0x20000000,ici,4), NumPut(1,NumPut(hAHK,ici,8),12), NumPut(idn-3,NumPut(idn-3,ici,12),24), NumPut(pt,ici,56,"int64")

   DllCall(VTable(pcm,4), "Uint", pcm, "Uint", &ici)   ; InvokeCommand
}

   DllCall("DestroyMenu", "Uint", hMenu)
   Release(pcm)
   CoUninitialize()
   Critical, Off
   return


Thanks for the suggestion from Sean, now DllGetClassObject (the first two lines)
Eucaly61 wrote:
Code:
* DllGetClassObject with IID_IClassFactory
* pcm := CreateInstance with "{000214E4-0000-0000-C000-000000000046}"   ; IContextMenu

* hMenu := DllCall("CreatePopupMenu")

are replaced by
Code:
pcm := CreateObject(CLSID,IID)


more shellex (or contextmenu) related CLSID and DLL could be found under below registry key
Code:
HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers
HKEY_CLASSES_ROOT\{file type or CLSID}\shellex\ContextMenuHandlers
HKEY_CLASSES_ROOT\Directory\shellex\ContextMenuHandlers
HKEY_CLASSES_ROOT\Directory\Background\shellex\ContextMenuHandlers

or even more in http://windowsxp.mvps.org/context_folders.htm#Directories
.
.
as for the magic number
Code:
DllCall(NumGet(NumGet(1*psf)+32) ...
DllCall(NumGet(NumGet(1*psf)+40) ...

Sean wrote:
... have to refer to header files for that. In this case of IShellFolder, for example, the infos are contained in ShObjIdl.h and/or ShObjIdl.idl.

There are on-line document to refer (for example,
Quote:
CreateViewObject and GetUIObjectOf under interface IShellFolder : IUnknown
or QueryContextMenu under interface IContextMenu : IUnknown

or
Quote:
http://www.koders.com/noncode/fid66A0E1FAB1C94FB665CFA5236DA4CDAEA22742CE.aspx#L1051

_________________
Eucaly61's DIY World (Mainly in Chinese)
http://eucaly61.blogspot.com/search/label/AutoHotKey
Back to top
View user's profile Send private message
Eucaly61



Joined: 16 May 2009
Posts: 18
Location: TW

PostPosted: Tue May 26, 2009 2:47 pm    Post subject: Reply with quote

I don't do further study, but I guess this would be helpful if some CLSID / DLL are not registed in registry key

that is, directly DllGetClassObject from DLL,

you could replace
Code:
   pcm := CreateObject(sClsId,sIId)

with
Code:
sDll := "some.dll"
If (GUID4String(sbinClassId, sClsId) And GUID4String(sbinIId, sIId))
   pcm := COM_CreateInstanceFromDll(sDll,sbinClassId,sbinIId)
else
   return

and, add below function define (a subset modified from ws4ahk.ahk)
Code:
COM_CreateInstanceFromDll(sDll, ByRef sbinClassId, ByRef sbinIId)
{
   static IID_IClassFactory := "{00000001-0000-0000-C000-000000000046}"

   if (!GUID4String(sbinIID_IClassFactory, IID_IClassFactory))
      return
   
   If (!Unicode4Ansi(wsDll, sDll))
      return       

   hDll := DllCall("ole32\CoLoadLibrary", "Str", wsDll, "Int", 1, "UInt")

   If (ErrorLevel <> 0) or (hDll = 0)
      Return

   iErr := DllCall(sDll . "\DllGetClassObject"
               ,"Str" , sbinClassId
               ,"Str" , sbinIID_IClassFactory
               ,"UInt*", pIFactory
               ,"Int")
   If iErr
      Return
   
   iObjPtr := COM_IClassFactory_CreateInstance(pIFactory, 0, sbinIId)
   
   Release (pIFactory)
   
   Return iObjPtr
}


COM_IClassFactory_CreateInstance(ppvIClassFactory, pUnkOuter, ByRef riid)
{
   iErr := DllCall(VTable(ppvIClassFactory, 3), "UInt", ppvIClassFactory
               , "UInt",  pUnkOuter
               , "Str",   riid
               , "Uint*", ppvObject
               , "Int")
   
   If iErr
      Return
   
   Return ppvObject
}

_________________
Eucaly61's DIY World (Mainly in Chinese)
http://eucaly61.blogspot.com/search/label/AutoHotKey
Back to top
View user's profile Send private message
Guest






PostPosted: Tue May 26, 2009 10:12 pm    Post subject: Reply with quote

@Eucaly61

I really appriciate that you cared to reply.
however I'm yet confused,
I tried to add ^1:: as hotkey but it gives me error. that { is not complete.

all I want is
say when my mouse has selected a file / folder
and I press something like tilde key or Insert key then
it launches this Sean's Context Menu for that file/folder
how do I do it?

Thanks in Advance
Back to top
Sean



Joined: 12 Feb 2007
Posts: 2462

PostPosted: Wed May 27, 2009 4:42 am    Post subject: Reply with quote

Eucaly61 wrote:
below is the demo code to get and invoke ContextMenu (ShellEx) items without bring up popup menu
Nice. Although I suppose it still may not be able to retrieve all the items of some submenus without bring up the pop-up menu, it can be used as an extension of InvokeVerb through COM. BTW, the GetMenu routine is familiar to me.
Back to top
View user's profile Send private message
joebodo



Joined: 28 Apr 2008
Posts: 48

PostPosted: Fri May 29, 2009 7:24 pm    Post subject: Reply with quote

Thanks for the information on getting and executing menu selections. I have been looking for this for awhile.

I am able to bring up the shell context menu for a file or a folder and execute the command with just a few problems.

I am unable to iterate through the "Send To" or the "Open With" submenus. I can get a handle to the submenu, but they don't appear to be populated.

When I select copy, the file is not being copied into the clipboard (appears that nothing happens as the clipboard has the text from before selecting that option).

I'm going to post my code as a reference for others:

The "menu" variable that I am using is for my own menu handling - hope it is not too confusing.

Code:
ShellContextMenu(menu, fileName) {

   CoInitialize()

   DllCall("shell32\SHParseDisplayName", "Uint", Unicode4Ansi(wPath,fileName)
      , "Uint", 0, "UintP", pidl, "Uint", 0, "Uint", 0)

   DllCall("shell32\SHBindToParent", "Uint", pidl
      , "Uint", GUID4String(IID_IShellFolder,"{000214E6-0000-0000-C000-000000000046}")
      , "UintP", psf, "UintP", pidlChild)
   DllCall(NumGet(NumGet(1*psf)+40), "Uint", psf
      , "Uint", 0, "Uint", 1, "UintP", pidlChild
      , "Uint", GUID4String(IID_IContextMenu,"{000214E4-0000-0000-C000-000000000046}")
      , "Uint", 0, "UintP", pcm)

   Release(psf)
   CoTaskMemFree(pidl)

   ; Create a stub menu
   hMenu := DllCall("CreatePopupMenu")

   ; Get the context menu
   DllCall(NumGet(NumGet(1*pcm)+12), "Uint", pcm, "Uint", hMenu, "Uint", menuNo, "Uint", 3, "Uint", 0x7FFF, "Uint", 0)

   menuAddSeparator(menu)
   addMenuEntries(hMenu, menu, fileName)
}

addMenuEntries(hMenu, menu, fileName) {

   Loop, % DllCall( "GetMenuItemCount", UInt,hMenu ) {
   
      idx := A_Index-1
      idn := DllCall( "GetMenuItemID", UInt,hMenu, Int,idx )
      nSize := DllCall( "GetMenuString"
              , UInt,hMenu, Int,idx, Int,0, Int,0, UInt,0x400 ) + 1
      VarSetCapacity( mStr,nSize )
      DllCall( "GetMenuString"
               , UInt,hMenu, Int,idx, Str,mStr, Int,nSize, UInt,0x400 )
      
      if (mstr <> "" AND idn = -1) {
         ; logA("submenu:" . hSub)
         ; hSub := DllCall("GetSubMenu",Uint,hMenu,int,idx)

; Not functioning properly...
         ; addMenuEntries(hSub, A_Args, mstr, idx, pcm)
      } else if (mstr = "") {
         menuAddSeparator(menu)
      } else if (idn != -1) {
         command := commandCreate("ContextMenu Run", "/file:" . fileName . " /idn:" . idn)
         menuAdd(menu, "/item:" . mstr, command)
      } else {
         menuAddSeparator(menu)
      }
   }
}
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 2462

PostPosted: Fri May 29, 2009 11:33 pm    Post subject: Reply with quote

As I said before, if the menu item appears in the main menu, not in the submenu, InvokeVerb is simpler to use.
Code:
sPath := A_ScriptFullPath ; path of the target file

SplitPath, sPath, sName, sDir
COM_Init()
psh := COM_CreateObject("Shell.Application")
COM_Invoke(psh, "NameSpace[" sDir "].ParseName[" sName "].InvokeVerb", "Copy")
COM_Release(psh)
COM_Term()
MsgBox, DONE
Back to top
View user's profile Send private message
skrommel



Joined: 30 Jul 2004
Posts: 190

PostPosted: Sun Jun 21, 2009 10:24 am    Post subject: Multiple files Reply with quote

Sean wrote:
LBJ wrote:
Has anyone managed to expand on this to work with a set of files? If so, how did you mange it?
How do you think Explorer does it? You can exactly do the same with the script.


Sad I'm having the same problem... Anyone know how to get this working with multiple files?

Skrommel
_________________
www.1HourSoftware.com
Back to top
View user's profile Send private message Visit poster's website
skwire



Joined: 18 Jan 2006
Posts: 273
Location: Conway, Arkansas

PostPosted: Sat Aug 01, 2009 11:02 pm    Post subject: Reply with quote

Sean, would it be possible to provide an example showing how to make this work with with a multiple file selection, please?
Back to top
View user's profile Send private message AIM Address Yahoo Messenger MSN Messenger
Sean



Joined: 12 Feb 2007
Posts: 2462

PostPosted: Sun Aug 02, 2009 1:27 am    Post subject: Reply with quote

All are described here. Notice that the function accepts an array of (child) pidls, meaning that you can pass an array of multiple child pidls instead of (an array of) single child pidl. That's what explorer does.
Back to top
View user's profile Send private message
temp01



Joined: 09 Jul 2009
Posts: 120

PostPosted: Fri Sep 25, 2009 9:35 am    Post subject: Reply with quote

Context menu for multiple items (files/folders):
Code:
#Persistent

ShellContextMenu("", "0x000D; 0x0027")   ; CSIDL_MYMUSIC; CSIDL_MYPICTURES
ShellContextMenu("C:\Windows\Media", "notify.wav; flourish.mid")
;ShellContextMenu("C:\Windows", "system32\; Cursors\; Media\")
Return

ShellContextMenu(parentDir, files){
   COM_CoInitialize()
   COM_GUID4String(IID_IShellFolder,"{000214E6-0000-0000-C000-000000000046}"), COM_GUID4String(IID_IContextMenu,"{000214E4-0000-0000-C000-000000000046}")
   
   parentDir := RegExReplace(parentDir, "\\$"), i:=0
   Loop, Parse, files, `;, %A_Space%%A_Tab%
   {
      If A_LoopField is Integer
         i++, DllCall("shell32\SHGetFolderLocation", "Uint", 0, "int", A_LoopField, "Uint", 0, "Uint", 0, "UintP", pidl%i%)
      Else IfNotExist, %parentDir%\%A_LoopField%
         Continue
      Else
         i++, DllCall("shell32\SHParseDisplayName", "Uint", COM_Unicode4Ansi(wPath,parentDir "\" A_LoopField), "Uint", 0, "UintP", pidl%i%, "Uint", 0, "Uint", 0)
      DllCall("shell32\SHBindToParent", "Uint", pidl%i%, "Uint", &IID_IShellFolder, "UintP", psf%i%, "UintP", pidlChild%i%)
   }
   IfEqual,i,0, return
   
   VarSetCapacity(apidl, i * 4, 0)
   Loop, %i%
      NumPut(pidlChild%A_Index%, apidl, (A_Index-1)*4, "UInt")
   
   DllCall(NumGet(NumGet(1*psf1)+40), "Uint", psf1, "Uint", 0, "Uint", i, "Uint", &apidl, "Uint", &IID_IContextMenu, "Uint", 0, "UintP", pcm)
   Loop, %i%
      COM_Release(psf%A_Index%), COM_CoTaskMemFree(pidl%A_Index%)
   
   hMenu := DllCall("CreatePopupMenu")
   DllCall(NumGet(NumGet(1*pcm)+12), "Uint", pcm, "Uint", hMenu, "Uint", 0, "Uint", 3, "Uint", 0x7FFF, "Uint", 0)   ; QueryContextMenu
   DetectHiddenWindows, On
   Process, Exist
   WinGet, hAHK, ID, ahk_pid %ErrorLevel%
   WinActivate, ahk_id %hAHK%
   Global   pcm2 := COM_QueryInterface(pcm,IID_IContextMenu2:="{000214F4-0000-0000-C000-000000000046}")
   Global   pcm3 := COM_QueryInterface(pcm,IID_IContextMenu3:="{BCFCE0A0-EC17-11D0-8D10-00A0C90F2719}")
   Global   WPOld:= DllCall("SetWindowLong", "Uint", hAHK, "int",-4, "int",RegisterCallback("WindowProc"))
   DllCall("GetCursorPos", "int64P", pt)
   DllCall("InsertMenu", "Uint", hMenu, "Uint", 0, "Uint", 0x0400|0x800, "Uint", 2, "Uint", 0)
   DllCall("InsertMenu", "Uint", hMenu, "Uint", 0, "Uint", 0x0400|0x002, "Uint", 1, "Uint", &parentDir)
   idn := DllCall("TrackPopupMenu", "Uint", hMenu, "Uint", 0x0100, "int", pt << 32 >> 32, "int", pt >> 32, "Uint", 0, "Uint", hAHK, "Uint", 0)
   NumPut(VarSetCapacity(ici,64,0),ici), NumPut(0x4000|0x20000000,ici,4), NumPut(1,NumPut(hAHK,ici,8),12), NumPut(idn-3,NumPut(idn-3,ici,12),24), NumPut(pt,ici,56,"int64")
   DllCall(NumGet(NumGet(1*pcm)+16), "Uint", pcm, "Uint", &ici)   ; InvokeCommand
   ;   VarSetCapacity(sName,259), DllCall(NumGet(NumGet(1*pcm)+20), "Uint", pcm, "Uint", idn-3, "Uint", 1, "Uint", 0, "str", sName, "Uint", 260)   ; GetCommandString
   DllCall("GlobalFree", "Uint", DllCall("SetWindowLong", "Uint", hAHK, "int", -4, "int", WPOld))
   DllCall("DestroyMenu", "Uint", hMenu)
   COM_Release(pcm3)
   COM_Release(pcm2)
   COM_Release(pcm)
   COM_CoUninitialize()
   pcm2:=pcm3:=WPOld:=0
}

WindowProc(hWnd, nMsg, wParam, lParam)
{
   Critical
   Global   pcm2, pcm3, WPOld
   If   pcm3
   {
      If   !DllCall(NumGet(NumGet(1*pcm3)+28), "Uint", pcm3, "Uint", nMsg, "Uint", wParam, "Uint", lParam, "UintP", lResult)
         Return   lResult
   }
   Else If   pcm2
   {
      If   !DllCall(NumGet(NumGet(1*pcm2)+24), "Uint", pcm2, "Uint", nMsg, "Uint", wParam, "Uint", lParam)
         Return   0
   }
   Return   DllCall("user32.dll\CallWindowProcA", "Uint", WPOld, "Uint", hWnd, "Uint", nMsg, "Uint", wParam, "Uint", lParam)
}
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 2462

PostPosted: Fri Sep 25, 2009 3:53 pm    Post subject: Reply with quote

Nice try, but too much redundancy here, dig a little further. And ; as the delimiter is not a good choice as ; is a valid character in a file name. What I'm using, for example, is like this.
Code:
ShellContextMenu("C:\Windows\test1.txt|test2.txt|test3.txt")
Back to top
View user's profile Send private message
HotKeyIt



Joined: 18 Jun 2008
Posts: 4653
Location: AHK Forum

PostPosted: Mon Nov 30, 2009 12:20 pm    Post subject: Reply with quote

Really great function, however it looks like "Copy" does not work, possibly due to absence of Explorer and marked file Sad
Could we redirect the function to script, to do the job?
_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun Wink


Last edited by HotKeyIt on Mon Nov 30, 2009 6:10 pm; edited 1 time in total
Back to top
View user's profile Send private message
Gauss



Joined: 10 Sep 2009
Posts: 203

PostPosted: Mon Nov 30, 2009 1:39 pm    Post subject: Reply with quote

The link for CoHelper.ahk is dead
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page Previous  1, 2, 3, 4, 5  Next
Page 3 of 5

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group