Совместил и переработал срипты:
1.ShellContextMenu.ahk by Sean
2.Get and invoke ContextMenu (ShellEx) items without bring up popup menu by Eucaly61
3.Выполнить пункт меню файла в папке, используя COM спасибо teadrinker, англ.InvokeVerb()
Добавление выделенных папок и файлов в AIMP3 работает. Возможно вызывать и другие пункты контекстного меню проводника применительно к выделенным папкам и файлам, но без недостатков не обошлось. С каждым вызовом меню происходит небольшая утечка памяти и объектов Gdi, от меня не зависящая, с которой было принято решение бороться с помощью перезагрузки скрипта, знаете другое решение - поделитесь.
Скрипт для получения (путём копирование в буфер обмена) списка доступных пунктов меню и визуализации самого меню. Вызываются команды вручную посредством клика, скрипт определяет ID команды и вызывает её. Последовательность пунктов меню и следовательно ID команд меняется, из-за чего автоматизация иногда бывает затруднена. Если вы видете что при вызове необходимого вам пункта меню для разных файлов и папок на разных дисках ID остаётся преждним, обычно это стандартные пункты "Свойства","Удалить","Создать ярлык"... , то можете использовать его во втором скрипте заменив функцию GetContexMenuCmdID("текст пункта меню|подменю") на полученный ID
/*
get and invoke ContextMenu (ShellEx) items without bring up popup menu
http://www.autohotkey.com/forum/post-271279.html#271279
IShellFolder::GetUIObjectOf
http://winapi.freetechsecrets.com/win32/WIN32IShellFolderGetUIObjectOf_Now_Su.htm
IContextMenu::QueryContextMenu
http://winapi.freetechsecrets.com/win32/WIN32IContextMenu.htm
Shell Interfaces
http://msdn.microsoft.com/en-us/library/windows/desktop/bb774328(v=vs.85).aspx
http://source.winehq.org/source/include/shobjidl.idl#L252
http://www.koders.com/noncode/fid66A0E1FAB1C94FB665CFA5236DA4CDAEA22742CE.aspx#L1051
http://www.netez.com/2xExplorer/shellFAQ/shmain.html
*/
DetectHiddenWindows, On
Process, Exist
WinGet, hAHK,ID,ahk_pid %ErrorLevel%
Reload_After:=20 ; боремся с утечкой ресурсов путём перезагрузки скрипта после определённого кол-ва вызовов меню
; если знаете, предложите другой способ ?
global hMenu,mStr,pcm,ici,riid_IShellFolder,riid_IContextMenu,mText
riid_IShellFolder:=GUID(IID_IShellFolder,"{000214E6-0000-0000-C000-000000000046}")
riid_IContextMenu:=GUID(IID_IContextMenu,"{000214E4-0000-0000-C000-000000000046}")
global pHandleMenuMsg,pcm2,pcm3,uIdSubclass:=1
pHandleMenuMsg:=RegisterCallback("HandleMenuMsg","Fast",6)
NumPut(VarSetCapacity(ici,16+5*A_PtrSize,0),ici)
NumPut(0x4000,ici,4) ; CMIC_MASK_UNICODE = 0x4000
NumPut(hAHK,ici,8)
^F11::
WinGet, ID,, A
WinGetClass, Class, A
if !(Class~="(Cabinet|Explore)WClass")
Return
oShell:=ComObjCreate("Shell.Application")
Loop % oShell.Windows.Count {
oIE:=oShell.Windows.Item(A_Index-1)
Sleep 10
} Until oIE.HWND=ID
oFolderItems:=oIE.Document.SelectedItems
pc:=oFolderItems.Count
if !pc {
MsgBox, Ничего не выделено!
oShell:=oIE:=oFolderItems:=""
Return
}
pl:=[]
VarSetCapacity(apidl,pc*A_PtrSize,0)
pc:=0 ; pointers count
hMenu:=DllCall("CreatePopupMenu")
for Item in oFolderItems {
DllCall("shell32\SHParseDisplayName","str",Item.Path,"ptr",0,"ptr*",_pidl,"uint",0,"uint*",0)
DllCall("shell32\SHBindToParent","ptr",_pidl,"ptr",riid_IShellFolder,"ptr*",pIShellFolder,"ptr*",pidl)
pl.Insert(_pidl)
NumPut(pidl,apidl,pc*A_PtrSize)
++pc
if (pc<>oFolderItems.Count) ; Release ?
DllCall(VTable(pIShellFolder,2),"ptr",pIShellFolder)
}
; IShellFolder::GetUIObjectOf
if DllCall(VTable(pIShellFolder,10),"ptr",pIShellFolder,"ptr",0,"uint",pc,"ptr",&apidl,"ptr",riid_IContextMenu,"ptr",0,"ptr*",pcm)
return
; IUnknown::QueryInterface
DllCall(VTable(pcm,0),"ptr",pcm,"ptr",GUID(IID_IContextMenu3,"{BCFCE0A0-EC17-11D0-8D10-00A0C90F2719}"),"ptr*",pcm3)
DllCall(VTable(pcm,0),"ptr",pcm,"ptr",GUID(IID_IContextMenu2,"{000214F4-0000-0000-C000-000000000046}"),"ptr*",pcm2)
if pcm {
; IContextMenu::QueryContextMenu ##### Утечка памяти и объектов Gdi !? #####
DllCall(VTable(pcm,3),"ptr",pcm,"ptr",hMenu,"uint",0,"uint",3,"uint",0x7FFF,"uint",0)
GetContexMenuCmdID("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") ; текст пункта меню длиной этой строки будет помещён в буфер обмена
subclassed:=DllCall("SetWindowSubclass","ptr",hAHK,"ptr",pHandleMenuMsg,"ptr",uIdSubclass,"ptr",0)
DllCall("GetCursorPos","int64P",pt) ; TPM_RETURNCMD = 0x100
TrayTip,,% "ID команды`nменю " miID:=DllCall("TrackPopupMenu","ptr",hMenu,"ptr",0x100,"int",pt<<32>>32,"int",pt>>32,"int",0,"ptr",hAHK,"ptr",0)
NumPut(miID-3,ici,8+A_PtrSize)
; IContextMenu::InvokeCommand
DllCall(VTable(pcm,4),"ptr",pcm,"ptr",&ici)
}
; Release
DllCall(VTable(pcm,2),"ptr",pcm3)
DllCall(VTable(pcm,2),"ptr",pcm2)
DllCall(VTable(pcm,2),"ptr",pcm)
DllCall(VTable(pIShellFolder,2),"ptr",pIShellFolder)
Loop % pl.MaxIndex()
DllCall("ole32\CoTaskMemFree","ptr",pl[A_Index])
if DllCall("RemoveWindowSubclass","ptr",hAHK,"ptr",pHandleMenuMsg,"ptr",uIdSubclass)
subclassed:=0
DllCall("DestroyMenu","ptr",hMenu)
oShell:=oIE:=oFolderItems:=""
Clipboard:=mText ; Поместим в буфер обмена текст пунктов меню у которых он получен
--Reload_After
if !Reload_After
Reload
Return
Esc::
if subclassed
DllCall("RemoveWindowSubclass","uint",hAHK,"uint",pHandleMenuMsg,"uint",uIdSubclass)
ExitApp
GetContexMenuCmdID(CmdName)
{
Loop, Parse, CmdName, |
{
nmatch:=A_Index
miText:=A_LoopField
nSize:=StrLen(miText)+2 ; "&" + 00
VarSetCapacity(mStr,nSize*2,0)
if (A_Index=1)
_hMenu:=hMenu
else _hMenu:=DllCall("GetSubMenu","ptr",_hMenu,"int",miN)
Loop, % DllCall("GetMenuItemCount","ptr",_hMenu)
{
miN:=A_Index-1, miID:=DllCall("GetMenuItemID","ptr",_hMenu,"int",miN)
DllCall("GetMenuStringW","ptr",_hMenu,"int",miN,"wstr",mStr,"int",nSize,"uint",0x400)
StringReplace, _mStr,mStr,&,,All
mText.=mStr "`n"
if (miText=_mStr) {
++match
Break
}
}
}
if (match=nmatch)
return miID
}
VTable(ptr, n) {
return NumGet(NumGet(ptr+0),n*A_PtrSize)
}
GUID(ByRef GUID, sGUID) ; Converts a string to a binary GUID and returns its address.
{
VarSetCapacity(GUID,16,0)
return DllCall("ole32\CLSIDFromString","wstr",sGUID,"ptr",&GUID) >= 0 ? &GUID : ""
}
HandleMenuMsg(hWnd,Msg,wParam,lParam,IdSubclass,dwRefData)
{
Critical
if (IdSubclass=1) {
If pcm3 {
If !DllCall(VTable(pcm3,7),"ptr", pcm3,"uint",Msg,"uint",wParam, "ptr",lParam,"uintP",lResult)
Return lResult
}
Else If pcm2 {
If !DllCall(VTable(pcm3,6),"ptr",pcm2,"uint",Msg,"uint",wParam,"ptr",lParam)
Return 0
}
}
Return DllCall("DefSubclassProc","uint",hWnd,"uint",Msg,"uint",wParam,"ptr",lParam)
}
/*
529 WM_ENTERMENULOOP
32 WM_SETCURSOR
278 WM_INITMENU
279 WM_INITMENUPOPUP
44 WM_MEASUREITEM
43 WM_DRAWITEM
289 WM_ENTERIDLE
287 WM_MENUSELECT
533 WM_CAPTURECHANGED
293
530 WM_EXITMENULOOP
*/
Рабочий скрипт, добвляет файлы и папки в AIMP3 посредством выделения в проводнике и нажатия Ctrl + F11
/*
get and invoke ContextMenu (ShellEx) items without bring up popup menu
http://www.autohotkey.com/forum/post-271279.html#271279
IShellFolder::GetUIObjectOf
http://winapi.freetechsecrets.com/win32/WIN32IShellFolderGetUIObjectOf_Now_Su.htm
IContextMenu::QueryContextMenu
http://winapi.freetechsecrets.com/win32/WIN32IContextMenu.htm
Shell Interfaces
http://msdn.microsoft.com/en-us/library/windows/desktop/bb774328(v=vs.85).aspx
http://source.winehq.org/source/include/shobjidl.idl#L252
http://www.koders.com/noncode/fid66A0E1FAB1C94FB665CFA5236DA4CDAEA22742CE.aspx#L1051
http://www.netez.com/2xExplorer/shellFAQ/shmain.html
*/
DetectHiddenWindows, On
Process, Exist
WinGet, hAHK,ID,ahk_pid %ErrorLevel%
Reload_After:=20 ; боремся с утечкой ресурсов путём перезагрузки скрипта после определённого кол-ва вызовов меню
; если знаете, предложите другой способ ?
global hMenu,mStr,pcm,ici,riid_IShellFolder,riid_IContextMenu
riid_IShellFolder:=GUID(IID_IShellFolder,"{000214E6-0000-0000-C000-000000000046}")
riid_IContextMenu:=GUID(IID_IContextMenu,"{000214E4-0000-0000-C000-000000000046}")
oShell:=ComObjCreate("Shell.Application")
NumPut(VarSetCapacity(ici,16+5*A_PtrSize,0),ici)
NumPut(0x4000,ici,4) ; CMIC_MASK_UNICODE = 0x4000
NumPut(hAHK,ici,8)
^F11::
WinGet, ID,, A
WinGetClass, Class, A
if !(Class~="(Cabinet|Explore)WClass")
Return
Loop % oShell.Windows.Count {
oIE:=oShell.Windows.Item(A_Index-1)
Sleep 10
} Until oIE.HWND=ID
oFolderItems:=oIE.Document.SelectedItems
pc:=oFolderItems.Count
if !pc {
MsgBox, Ничего не выделено!
oShell:=oIE:=oFolderItems:=""
Return
}
pl:=[]
VarSetCapacity(apidl,pc*A_PtrSize,0)
pc:=0 ; pointers count
hMenu:=DllCall("CreatePopupMenu")
for Item in oFolderItems {
DllCall("shell32\SHParseDisplayName","str",Item.Path,"ptr",0,"ptr*",_pidl,"uint",0,"uint*",0)
DllCall("shell32\SHBindToParent","ptr",_pidl,"ptr",riid_IShellFolder,"ptr*",pIShellFolder,"ptr*",pidl)
pl.Insert(_pidl)
NumPut(pidl,apidl,pc*A_PtrSize)
++pc
if (pc<>oFolderItems.Count) ; Release ?
DllCall(VTable(pIShellFolder,2),"ptr",pIShellFolder)
}
; IShellFolder::GetUIObjectOf
if DllCall(VTable(pIShellFolder, 10),"ptr",pIShellFolder,"ptr",0,"uint",pc,"ptr",&apidl,"ptr",riid_IContextMenu,"ptr",0,"ptr*",pcm)
return
if pcm {
; IContextMenu::QueryContextMenu ##### Утечка памяти и объектов Gdi !? #####
DllCall(VTable(pcm,3),"ptr",pcm,"ptr",hMenu,"uint",0,"uint",3,"uint",0x7FFF,"uint",0)
; DllCall("GetCursorPos","int64P",pt) ; TPM_RETURNCMD = 0x100
; TrayTip,,% "ID команды`nменю " miID:=DllCall("TrackPopupMenu","ptr",hMenu,"ptr",0x100,"int",pt<<32>>32,"int",pt>>32,"int",0,"ptr",hAHK,"ptr",0)
if miID:=GetContexMenuCmdID("AIMP3|Добавить в плейлист") { ; парсим текст меню и подменю для получения ID команды, ID имеет свойство меняться
NumPut(miID-3,ici,8+A_PtrSize)
; ContextMenu::InvokeCommand
DllCall(VTable(pcm,4),"ptr",pcm,"ptr",&ici)
}
}
; Release
DllCall(VTable(pcm,2),"ptr",pcm)
DllCall(VTable(pIShellFolder,2),"ptr",pIShellFolder)
Loop % pl.MaxIndex()
DllCall("ole32\CoTaskMemFree","ptr",pl[A_Index])
DllCall("DestroyMenu","ptr",hMenu)
oIE:=oFolderItems:=""
--Reload_After
if !Reload_After
Reload
Return
Esc::ExitApp
GetContexMenuCmdID(CmdName)
{
Loop, Parse, CmdName, |
{
nmatch:=A_Index
miText:=A_LoopField
nSize:=StrLen(miText)+2 ; "&" + 00
VarSetCapacity(mStr,nSize*2,0)
if (A_Index=1)
_hMenu:=hMenu
else _hMenu:=DllCall("GetSubMenu","ptr",_hMenu,"int",miN)
Loop, % DllCall("GetMenuItemCount","ptr",_hMenu)
{
miN:=A_Index-1, miID:=DllCall("GetMenuItemID","ptr",_hMenu,"int",miN)
DllCall("GetMenuStringW","ptr",_hMenu,"int",miN,"wstr",mStr,"int",nSize,"uint",0x400)
StringReplace, _mStr,mStr,&,,All
;Clipboard.=mStr "`n"
if (miText=_mStr) {
++match
Break
}
}
}
if (match=nmatch)
return miID
}
VTable(ptr, n) {
return NumGet(NumGet(ptr+0),n*A_PtrSize)
}
GUID(ByRef GUID, sGUID) ; Converts a string to a binary GUID and returns its address.
{
VarSetCapacity(GUID,16,0)
return DllCall("ole32\CLSIDFromString","wstr",sGUID,"ptr",&GUID) >= 0 ? &GUID : ""
}
Оба срипта могут использоваться если закрыть глаза на утечку ресурсов
Скрипт был опробован на двух компьютерах под Windows XP SP3, AutoHotkey_L x86 Unicode, но возможно будет работать и на x64
Если кому необходимо, скрипт возможно приспособить и для AHK Basic
CoHelper.ahk
/*
CoHelper.ahk
*/
VTable(ppv, idx)
{
Return DecodeInteger(DecodeInteger(ppv) + idx * 4)
}
DecodeInteger(ptr)
{
Return *ptr | *++ptr << 8 | *++ptr << 16 | *++ptr << 24
}
EncodeInteger(ref, val)
{
DllCall("ntdll\RtlFillMemoryUlong", "Uint", ref, "Uint", 4, "Uint", val)
}
Ansi2Unicode(ByRef sString, ByRef wString, nLen = 0)
{
If !nLen
nLen := DllCall("MultiByteToWideChar"
, "Uint", 0
, "Uint", 0
, "Uint", &sString
, "int", -1
, "Uint", 0
, "int", 0)
VarSetCapacity(wString, nLen * 2)
DllCall("MultiByteToWideChar"
, "Uint", 0
, "Uint", 0
, "Uint", &sString
, "int", -1
, "Uint", &wString
, "int", nLen)
}
Unicode2Ansi(ByRef wString, ByRef sString, nLen = 0)
{
pString := wString + 0 > 65535 ? wString : &wString
If !nLen
nLen := DllCall("WideCharToMultiByte"
, "Uint", 0
, "Uint", 0
, "Uint", pString
, "int", -1
, "Uint", 0
, "int", 0
, "Uint", 0
, "Uint", 0)
VarSetCapacity(sString, nLen)
DllCall("WideCharToMultiByte"
, "Uint", 0
, "Uint", 0
, "Uint", pString
, "int", -1
, "str", sString
, "int", nLen
, "Uint", 0
, "Uint", 0)
}
CLSID4ProgID(Byref CLSID, sProgID)
{
VarSetCapacity(CLSID, 16)
Ansi2Unicode(sProgID, wProgID)
DllCall("ole32\CLSIDFromProgID", "str", wProgID, "str", CLSID)
}
GUID4String(Byref CLSID, sString)
{
VarSetCapacity(CLSID, 16, 0)
Ansi2Unicode(sString, wString, 39)
DllCall("ole32\CLSIDFromString", "str", wString, "str", CLSID)
}
String4GUID(Byref CLSID)
{
VarSetCapacity(wString, 39 * 2)
DllCall("ole32\StringFromGUID2", "str", CLSID, "str", wString, "int", 39)
Unicode2Ansi(wString, sString, 39)
Return sString
}
CreateObject(ByRef CLSID, ByRef IID, CLSCTX = 5)
{
If ( StrLen(CLSID) = 38 )
GUID4String(CLSID, CLSID)
If ( StrLen( IID) = 38 )
GUID4String( IID, IID)
DllCall("ole32\CoCreateInstance", "str", CLSID, "Uint", 0, "Uint", CLSCTX, "str", IID, "UintP", ppv)
Return ppv
}
GetObject(Namespace)
{
Ansi2Unicode(Namespace, wNamespace)
GUID4String(IID_IDispatch, "{00020400-0000-0000-C000-000000000046}")
DllCall("ole32\CoGetObject", "str", wNamespace, "Uint", 0, "str", IID_IDispatch, "UintP", pdisp)
Return pdisp
}
GetActiveObject(ProgID)
{
CLSID4ProgID(CLSID, ProgID)
DllCall("oleaut32\GetActiveObject", "str", CLSID, "Uint", 0, "UintP", punk)
Return punk
}
SysAllocString(sString)
{
Ansi2Unicode(sString, wString)
Return DllCall("oleaut32\SysAllocString", "str", wString)
}
SysFreeString(pString)
{
Return DllCall("oleaut32\SysFreeString", "Uint", pString)
}
OleInitialize()
{
DllCall("ole32\OleInitialize", "Uint", 0)
}
OleUninitialize()
{
DllCall("ole32\OleUninitialize")
}
CoInitialize()
{
DllCall("ole32\CoInitialize", "Uint", 0)
}
CoUninitialize()
{
DllCall("ole32\CoUninitialize")
}
QueryInterface(ppv, ByRef IID)
{
If ( StrLen(IID) = 38 )
GUID4String(IID, IID)
DllCall(DecodeInteger(DecodeInteger(ppv)), "Uint", ppv, "str", IID, "UintP", ppv)
Return ppv
}
AddRef(ppv)
{
Return DllCall(DecodeInteger(DecodeInteger(ppv) + 4), "Uint", ppv)
}
Release(ppv)
{
Return DllCall(DecodeInteger(DecodeInteger(ppv) + 8), "Uint", ppv)
}
QueryService(ppv, ByRef SID, ByRef IID)
{
If ( StrLen(SID) = 38 )
GUID4String(SID, SID)
If ( StrLen(IID) = 38 )
GUID4String(IID, IID)
GUID4String(IID_IServiceProvider, "{6D5140C1-7436-11CE-8034-00AA006009FA}")
DllCall(DecodeInteger(DecodeInteger(ppv)), "Uint", ppv, "str", IID_IServiceProvider, "UintP", psp)
DllCall(DecodeInteger(DecodeInteger(psp) + 12), "Uint", psp, "str", SID, "str", IID, "UintP", ppv)
DllCall(DecodeInteger(DecodeInteger(psp) + 8), "Uint", psp)
Return ppv
}