Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Invoking directly ContextMenu of Files and Folders


  • Please log in to reply
62 replies to this topic
Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

Really great function, however it looks like "Copy" does not work, possibly due to absence of Explorer and marked file :(

What you meant by not working? Copy menu item has nothing to do with explorer.

Could we redirect the function to script, to do the job?

Sure you can do, even insert your own menu items.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

The link for CoHelper.ahk is dead

Not just dead, it was deleted from the storage since some users had constantly annoyed by still posting questions on this library which became obsolete almost two years ago. And, this script is no longer maintained.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008

Really great function, however it looks like "Copy" does not work, possibly due to absence of Explorer and marked file :(

What you meant by not working? Copy menu item has nothing to do with explorer.


Yes it just does not do anything, does it work for you :?:

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

Yes it just does not do anything, does it work for you :?:

What do you expect to happen? Open explorer or any other file manager and go to arbitrary directory and right click on empty space then select paste. What happens?

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008

Yes it just does not do anything, does it work for you :?:

What do you expect to happen? Open explorer or any other file manager and go to arbitrary directory and right click on empty space then select paste. What happens?

When I go to explorer and right mouse click a file, select copy, then run following and select paste, it works:
#Persistent
sPath = %A_Temp%
ShellContextMenu(sPath<>"" ? sPath : 0x0011)
Return
But when I run below one, select copy, go to explorer and right mouse click on a folder or empty space, it does not work, paste is grayed out. :(
Just tested on my second computer and it does not work as well, both XP SP3
#Persistent
sPath = %A_ProgramFiles%\Internet Explorer\iexplore.exe
ShellContextMenu(sPath<>"" ? sPath : 0x0011)
Return


Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

But when I run below one, select copy, go to explorer and right mouse click on a folder or empty space, it does not work, paste is grayed out. :(

It works fine in my system (after changed appropriately to accord with unicode build), but, I'm using AutoHotkeyU.exe currently. Actually it's a little customized by me, but I don't think those are relevant here. So, I'm not sure if it's an issue of AHK itself or the script atm.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
OK, I realized you might not port it to COM.ahk, right? Then, you must use OleInitialize/OleUninitialize, not CoInitialize/CoUninitialize, to make Copy work. Actually one of my customization was that always OleInitialize/OleUninitialize at the start-up/termination of AHK.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Thank you very much, it works like a charm now :)

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008

Thank you very much, it works like a charm now :)


This is what I use now:
#Persistent
ShellContextMenu(A_Temp)
Return

ShellContextMenu(sPath)
{
   DllCall("ole32\OleInitialize", "Uint", 0)
   If   sPath Is Not Integer
      DllCall("shell32\SHParseDisplayName", "Uint", Unicode4Ansi(wPath,sPath), "Uint", 0, "UintP", pidl, "Uint", 0, "Uint", 0)
   Else   DllCall("shell32\SHGetFolderLocation", "Uint", 0, "int", sPath, "Uint", 0, "Uint", 0, "UintP", pidl)
   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)

   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 := QueryInterface(pcm,IID_IContextMenu2:="{000214F4-0000-0000-C000-000000000046}")
   Global   pcm3 := 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", &sPath)
   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)
   Release(pcm3)
   Release(pcm2)
   Release(pcm)
   DllCall("ole32\OleUnInitialize", "Uint", 0)
   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)
}
VTable(ppv, idx)
{
	Return	NumGet(NumGet(1*ppv)+4*idx)
}
QueryInterface(ppv, ByRef IID)
{
	If	StrLen(IID)=38
		GUID4String(IID,IID)
	DllCall(NumGet(NumGet(1*ppv)), "Uint", ppv, "str", IID, "UintP", ppv)
	Return	ppv
}
AddRef(ppv)
{
	Return	DllCall(NumGet(NumGet(1*ppv)+4), "Uint", ppv)
}
Release(ppv)
{
	Return	DllCall(NumGet(NumGet(1*ppv)+8), "Uint", ppv)
}
GUID4String(ByRef CLSID, String)
{
	VarSetCapacity(CLSID, 16)
	DllCall("ole32\CLSIDFromString", "Uint", Unicode4Ansi(String,String,38), "Uint", &CLSID)
	Return	&CLSID
}
CoTaskMemAlloc(cb)
{
	Return	DllCall("ole32\CoTaskMemAlloc", "Uint", cb)
}
CoTaskMemFree(pv)
{
	Return	DllCall("ole32\CoTaskMemFree", "Uint", pv)
}
Unicode4Ansi(ByRef wString, sString, nSize = "")
{
	If (nSize = "")
	    nSize:=DllCall("kernel32\MultiByteToWideChar", "Uint", 0, "Uint", 0, "Uint", &sString, "int", -1, "Uint", 0, "int", 0)
	VarSetCapacity(wString, nSize * 2 + 1)
	DllCall("kernel32\MultiByteToWideChar", "Uint", 0, "Uint", 0, "Uint", &sString, "int", -1, "Uint", &wString, "int", nSize + 1)
	Return	&wString
}


fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
Works nice, does anyone see a way to bring up the context menu which you get when you click on empty space in explorer?

Specifically, I would like to call the "new folder", "new text file", and "next desktop background" (win7 only?) actions. While we're at that, any proper way for calling specific items? I saw that some can be executed through COM apparently, but I suppose this doesn't work for the "empty selection" context menu?

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
Don't blindly post a question. Don't you think at least have to read/scan the whole thread before posting a question to it?

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
I did, but I only found code for COHelper.ahk versions, and I don't know how to port this properly to COM.ahk.

Right now I'm trying this approach, with some more success: <!-- m -->http://www.autohotke... ... 925#319925<!-- m -->

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
I now finally managed to execute a context menu item from the empty selection desktop context menu without showing the menu.

I really can't say I understood the whole code, I have no idea how the memory addresses of functions and structs were extracted from the winapi headers, but atleast I managed to modify the code to get it to do what I want :)

Here's the code I used. It isn't very clean, but I wanted to share my success:
#Persistent
/*
;enable Debug view
	a_scriptPID := DllCall("GetCurrentProcessId")	; get script's PID
	ifwinexist, DebugView on ; kill it if the debug viewer is running from an older instance
		{
		winactivate, DebugView on
		Winwaitactive, DebugView on
		winclose, DebugView on
		}
	run, %A_ScriptDir%\DebugView\Dbgview.exe /f
	winwait, DebugView on
	winactivate, DebugView on
	Winwaitactive, DebugView on
	sendinput, !E{down}{down}{down}{down}{down}{Enter}
	winwait, DebugView Filter
	winactivate, DebugView Filter
	Winwaitactive, DebugView Filter 
	mouseclick, left, 125, 85
	send, [%a_scriptPID%*{Enter}
*/

/*
Executes context menu entries of shell items without showing their menus
Usage:
ShellContextMenu("Desktop",1)			;Calls "Next Desktop background" in Win7
1st parameter can be "Desktop" for empty selection desktop menu, a path, or an idl
Leave 2nd parameter empty to show context menu and extract idn by clicking on an entry (shows up in debugview)
*/ 
ShellContextMenu(sPath,idn) 
{ 
   DllCall("ole32\OleInitialize", "Uint", 0) 
   if (spath="Desktop")
   {
		 DllCall("shell32\SHGetDesktopFolder", "UintP", psf) 
		 DllCall(NumGet(NumGet(1*psf)+32), "Uint", psf, "Uint", 0, "Uint", GUID4String(IID_IContextMenu,"{000214E4-0000-0000-C000-000000000046}"), "UintP", pcm) 
	 }
   else
   {
		 If   sPath Is Not Integer 
	      DllCall("shell32\SHParseDisplayName", "Uint", Unicode4Ansi(wPath,sPath), "Uint", 0, "UintP", pidl, "Uint", 0, "Uint", 0) 
	   Else   DllCall("shell32\SHGetFolderLocation", "Uint", 0, "int", sPath, "Uint", 0, "Uint", 0, "UintP", pidl) 
	   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) 

   hMenu := DllCall("CreatePopupMenu") 
   idnMIN=1
   DllCall(NumGet(NumGet(1*pcm)+12), "Uint", pcm, "Uint", hMenu, "Uint", 0, "Uint", idnMIN, "Uint", 0x7FFF, "Uint", 0)   ; QueryContextMenu
	  

   DetectHiddenWindows, On 
   Process, Exist 
   WinGet, hAHK, ID, ahk_pid %ErrorLevel% 
   if !idn
   {
	   WinActivate, ahk_id %hAHK% 	   
	   Global   pcm2 := QueryInterface(pcm,IID_IContextMenu2:="{000214F4-0000-0000-C000-000000000046}") 
	   Global   pcm3 := 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", &sPath) 
	   idn2 := DllCall("TrackPopupMenu", "Uint", hMenu, "Uint", 0x0100, "int", pt << 32 >> 32, "int", pt >> 32, "Uint", 0, "Uint", hAHK, "Uint", 0)
	 }
	 else
	 	 idn2:=idn
   NumPut(VarSetCapacity(ici,64,0),ici)
	 NumPut(0x4000|0x20000000,ici,4) 
	 NumPut(1,NumPut(hAHK,ici,8),12)
	 NumPut(idn2-idnMIN,NumPut(idn2-idnMIN,ici,12),24)
	 if !idn
	 	NumPut(pt,ici,56,"int64") 
   DllCall(NumGet(NumGet(1*pcm)+16), "Uint", pcm, "Uint", &ici)   ; InvokeCommand 
   if !idn
   {
	 VarSetCapacity(sName,259), DllCall(NumGet(NumGet(1*pcm)+20), "Uint", pcm, "Uint", idn2-idnMIN, "Uint", 1, "Uint", 0, "str", sName, "Uint", 260)   ; GetCommandString
	 outputdebug command string: %sname% idn: %idn2%
   DllCall("GlobalFree", "Uint", DllCall("SetWindowLong", "Uint", hAHK, "int", -4, "int", WPOld)) 
   
   Release(pcm3) 
   Release(pcm2) 
   }
   DllCall("DestroyMenu", "Uint", hMenu) 
   Release(pcm) 
   DllCall("ole32\OleUnInitialize", "Uint", 0) 
   ;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) 
} 
VTable(ppv, idx) 
{ 
   Return   NumGet(NumGet(1*ppv)+4*idx) 
} 
QueryInterface(ppv, ByRef IID) 
{ 
   If   StrLen(IID)=38 
      GUID4String(IID,IID) 
   DllCall(NumGet(NumGet(1*ppv)), "Uint", ppv, "str", IID, "UintP", ppv) 
   Return   ppv 
} 
AddRef(ppv) 
{ 
   Return   DllCall(NumGet(NumGet(1*ppv)+4), "Uint", ppv) 
} 
Release(ppv) 
{ 
   Return   DllCall(NumGet(NumGet(1*ppv)+8), "Uint", ppv) 
} 
GUID4String(ByRef CLSID, String) 
{ 
   VarSetCapacity(CLSID, 16) 
   DllCall("ole32\CLSIDFromString", "Uint", Unicode4Ansi(String,String,38), "Uint", &CLSID) 
   Return   &CLSID 
} 
CoTaskMemAlloc(cb) 
{ 
   Return   DllCall("ole32\CoTaskMemAlloc", "Uint", cb) 
} 
CoTaskMemFree(pv) 
{ 
   Return   DllCall("ole32\CoTaskMemFree", "Uint", pv) 
} 
Unicode4Ansi(ByRef wString, sString, nSize = "") 
{ 
   If (nSize = "") 
       nSize:=DllCall("kernel32\MultiByteToWideChar", "Uint", 0, "Uint", 0, "Uint", &sString, "int", -1, "Uint", 0, "int", 0) 
   VarSetCapacity(wString, nSize * 2 + 1) 
   DllCall("kernel32\MultiByteToWideChar", "Uint", 0, "Uint", 0, "Uint", &sString, "int", -1, "Uint", &wString, "int", nSize + 1) 
   Return   &wString 
}

Read function comment to see how to acquire idn.

I Hope this might help others, I for one had a real hard time figuring this out. Here's some good documentation on this btw: <!-- m -->http://www.netez.com... ... ntext.html<!-- m -->

Two questions remain for me though:
Do I need to call OleInitialize and OleUnInitialize when I'm using COM.ahk in other parts of my script?
I tried acquiring idn of a submenu and calling above function with that (namy new->textfile). However, I don't see any result. I'm getting different idns for submenu entries, which is why I believe this should be working. I saw a post before about new context menu not being populated when called directly without context, but I don't believe this is relevant here, right? Any ideas?

Update, had a few irrelevant functions in there.

Deo
  • Members
  • 199 posts
  • Last active: Jan 31 2014 03:19 PM
  • Joined: 16 May 2010
Why it may not work on folder, but files only?
I use Win 7 64bit and a code from HotKeyIt's post above

sbc
  • Members
  • 321 posts
  • Last active: Jun 07 2011 10:24 AM
  • Joined: 25 Aug 2009

As I said before, if the menu item appears in the main menu, not in the submenu, InvokeVerb is simpler to use.

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

I've been trying to apply a filter for multiple files without success. Would you take a look at my code? :?
Dirpath := "D:\Scripts\Test\COM"
objShell := ComObjCreate("Shell.Application")
objFolder := objShell.NameSpace(Dirpath)      
objFolderItems := objFolder.items
[color=red]objFolderItems := objFolderItems.Filter(0x00040, "*.txt")[/color]	; SHCONTF_NONFOLDERS = 64   ;this is the line which doesn't work
objFolderItems.InvokeVerbEx("Copy")
(*the code is for AHKL)