- I do not have any plans to further develop this, although I welcome any efforts by others to potentially extend this.
- I was able to get this to work on Spek and LibreOffice Writer. Warning: it could potentially crash those programs, and it did crash other programs.
- The idea is to be able to bypass the Open dialog, to avoid having to use it.
- Credits to qwerty12 for collaborating with me, and to HotKeyIt for his InjectAhkDll function and AutoHotkeyMini dlls. Note: this script works with AutoHotkey_L v1.1, and doesn't require AutoHotkey_H.
Code: Select all
;==================================================
;drag-and-drop programmatically
;(by using OleDropTargetInterface)
;(and not WM_DROPFILES)
;(tested on Spek)
;how to use:
;you need AutoHotkey v1.1 U32 (note: Spek is 32-bit)
;you need the AutoHotkeyMini w32 dll (see the link below)
;(you do not need AutoHotkey_H)
;you need an mp3
;you need Spek (around 8MB)
;Spek – Free Acoustic Spectrum Analyzer / Spectrogram Viewer
;http://spek.cc/
;this injects the AutoHotkey dll into the program,
;warning: dll injection can potentially crash a program,
;warning: various programs crashed when I tried this,
;note: to drag-and-drop onto an x64/x32 process,
;the bitnesses of the AutoHotkey exe and AutoHotkeyMini dll must match the process
;==================================================
;[get x64 and x32 versions of AutoHotkeyMini.dll]
;GitHub - HotKeyIt/ahkdll-v1-release: AutoHotkey_H v1 release
;https://github.com/HotKeyIt/ahkdll-v1-release
vDir := "C:\Program Files\AutoHotkey"
if (A_PtrSize = 8)
vPathDll := vDir "\AutoHotkeyMiniU64.dll"
else
vPathDll := vDir "\AutoHotkeyMiniU32.dll"
;==================================================
;tested on Windows 7, AHK v1.1.30.00 U32, Spek (x86) and LibreOffice Writer (x86)
q:: ;drag-and-drop file onto program (if the bitnesses match, and if a drop target interface is available)
WinGet, hWnd, ID, A
WinGet, vPID, PID, % "ahk_id " hWnd
WinGet, vPName, ProcessName, % "ahk_id " hWnd
;if !(vPName = "spek.exe")
; return
if !(JEE_ProcessIs64Bit(vPID) = (A_PtrSize=8))
{
MsgBox, % "error: process bitness mismatch"
return
}
pDropTarget := DllCall("user32\GetProp", Ptr,hWnd, Str,"OleDropTargetInterface", Ptr)
MsgBox, % pDropTarget
if !pDropTarget
return
;pipe-separated list of paths
filenames := A_Desktop "\MyMp3.mp3"
filenames := A_ScriptDir "\MyMp3.mp3"
vScript1 := "hWnd := " hWnd
vScript1 .= "`r`n" "filenames := " Chr(34) filenames Chr(34)
Gosub SubGetScript
;MsgBox, % vScript1 "`r`n" vScript2
rThread := InjectAhkDll(vPID, vPathDll)
rThread.Exec(vScript1 "`r`n" vScript2)
rThread := ""
return
;==================================================
;based on code by qwerty12:
;SHMultiFileProperties - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=5&t=30483&p=145200#p145200
SubGetScript:
;verbatim script
vScript2 = ;continuation section
(% ` Join`r`n
filenames := StrSplit(filenames, "|")
path := filenames.1
SplitPath, path,, dir
for key, path in filenames
{
SplitPath, path, name
filenames[key] := name
}
if (!IsObject(filenames) || !filenames.MaxIndex() || !filenames[1])
return
if (!(pUnk := DllCall("GetProp", "Ptr", hWnd, "Str", "OleDropTargetInterface", "Ptr")))
return
if (!VarSetCapacity(IID_IDataObject))
VarSetCapacity(IID_IDataObject, 16)
, DllCall("ole32\CLSIDFromString", "WStr", "{0000010e-0000-0000-C000-000000000046}", "Ptr", &IID_IDataObject)
if (!VarSetCapacity(IID_IShellFolder))
VarSetCapacity(IID_IShellFolder, 16)
, DllCall("ole32\CLSIDFromString", "WStr", "{000214E6-0000-0000-C000-000000000046}", "Ptr", &IID_IShellFolder)
DllCall("shell32\SHGetDesktopFolder", "Ptr*", pDesktop)
if (dir = A_Desktop)
pISF := pDesktop
else
DllCall("shell32\SHParseDisplayName", "WStr", dir, "Ptr", 0, "Ptr*", pidl_dir, UInt, 0, Ptr, 0)
, DllCall(NumGet(NumGet(pDesktop+0)+5*A_PtrSize), "Ptr", pDesktop, "Ptr", pidl_dir, "UInt", 0, "Ptr", &IID_IShellFolder, "Ptr*", pISF) ; BindToObject
if (pISF)
{
pDropTarget := ComObjQuery(pUnk, "{00000122-0000-0000-C000-000000000046}")
VarSetCapacity(pidl_list, filenames.MaxIndex() * A_PtrSize)
filenameCount := 0, ParseDisplayName := NumGet(NumGet(pISF+0)+3*A_PtrSize)
for _, filename in filenames
filenameCount += DllCall(ParseDisplayName, "Ptr", pISF, "Ptr", 0, Ptr, 0, "WStr", filename, "Ptr", 0, "Ptr", &pidl_list+(filenameCount * A_PtrSize), "Ptr", 0) >= 0x00
if (filenameCount)
{
DllCall(NumGet(NumGet(pISF+0)+10*A_PtrSize), "Ptr", pISF, "Ptr", 0, "UInt", filenameCount, "Ptr", &pidl_list, "Ptr", &IID_IDataObject, "Ptr", 0, "Ptr*", pDataObject) ; GetUIObjectOf
if (pDataObject)
DllCall(NumGet(NumGet(pDropTarget+0)+3*A_PtrSize), "Ptr", pDropTarget, "Ptr", pDataObject, "UInt", 0, "Int64", 0, "UInt*", 1) ; DragEnter
, ret := DllCall(NumGet(NumGet(pDropTarget+0)+6*A_PtrSize), "Ptr", pDropTarget, "Ptr", pDataObject, "UInt", 0, "Int64", 0, "UInt*", 1) >= 0x00 ; Drop
, ObjRelease(pDataObject)
Loop, % filenameCount
if ((pidl := NumGet(pidl_list, (A_Index - 1) * A_PtrSize, "Ptr")))
DllCall("ole32\CoTaskMemFree", "Ptr", pidl)
}
DllCall("ole32\CoTaskMemFree", "Ptr", pidl_dir)
for _, obj in [pDropTarget, pISF]
ObjRelease(obj)
if !(dir = A_Desktop)
ObjRelease(pDesktop)
}
)
return
;==================================================
JEE_ProcessIs64Bit(vPIDOrName:="")
{
if !(vPID := ProcessExist(vPIDOrName))
return
if !A_Is64bitOS
return 0
;PROCESS_QUERY_INFORMATION := 0x400
hProc := DllCall("kernel32\OpenProcess", UInt,0x400, Int,0, UInt,vPID, Ptr)
DllCall("kernel32\IsWow64Process", Ptr,hProc, IntP,vIsWow64Process)
DllCall("kernel32\CloseHandle", Ptr,hProc)
return !vIsWow64Process
}
;commands as functions (AHK v2 functions for AHK v1) - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=37&t=29689
ProcessExist(PIDorName:="")
{
Process Exist, %PIDorName%
return ErrorLevel
}
;==================================================
;[original version of InjectAhkDll]
;[SOLVED]get other process's working dir - Page 3 - Ask for Help - AutoHotkey Community
;https://autohotkey.com/board/topic/85304-solvedget-other-processs-working-dir/page-3#entry544650
;[get x64 and x32 versions of AutoHotkeyMini.dll]
;GitHub - HotKeyIt/ahkdll-v1-release: AutoHotkey_H v1 release
;https://github.com/HotKeyIt/ahkdll-v1-release
;by HotKeyIt (modified version by jeeswg to not require _Struct.ahk)
InjectAhkDll(PID,dll:="AutoHotkey.dll",script:=0)
{
static PROCESS_ALL_ACCESS:=0x1F0FFF,MEM_COMMIT := 0x1000,MEM_RELEASE:=0x8000,PAGE_EXECUTE_READWRITE:=64
,hKernel32:=DllCall("LoadLibrary","Str","kernel32.dll","PTR"),LoadLibraryA:=DllCall("GetProcAddress","PTR",hKernel32,"AStr","LoadLibraryA","PTR")
,base:={__Call:"InjectAhkDll",__Delete:"InjectAhkDll"},FreeLibrary:=DllCall("GetProcAddress","PTR",hKernel32,"AStr","FreeLibrary","PTR")
static TH32CS_SNAPMODULE:=0x00000008,INVALID_HANDLE_VALUE:=-1
,MAX_PATH:=260,MAX_MODULE_NAME32:=255,ModuleName:="",init:=VarSetCapacity(ModuleName,MAX_PATH*(A_IsUnicode?2:1))
if IsObject(PID)
{
if (dll!="Exec" && script)
return DllCall("MessageBox","PTR",0,"Str","Only Exec method can be used here!","STR","Error","UInt",0)
hProc := DllCall("OpenProcess", "UInt", PROCESS_ALL_ACCESS, "Int",0, "UInt", PID.PID,"PTR")
if !hProc
return DllCall("MessageBox","PTR",0,"Str","Could not open process for PID: " PID.PID,"STR","Error","UInt",0)
if (!script) ; Free Library in remote process (object is being deleted)
{
; Terminate the thread in ahkdll
hThread := DllCall("CreateRemoteThread", "PTR", hProc, "PTR", 0, "PTR", 0, "PTR", PID.ahkTerminate, "PTR", 0, "UInt", 0, "PTR", 0,"PTR")
DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)
,DllCall("CloseHandle", "PTR", hThread)
; Free library in remote process
hThread := DllCall("CreateRemoteThread", "PTR", hProc, "UInt", 0, "UInt", 0, "PTR", FreeLibrary, "PTR", PID.hModule, "UInt", 0, "UInt", 0,"PTR")
DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)
,DllCall("CloseHandle", "PTR", hThread),DllCall("CloseHandle", "PTR", hProc)
return
}
nScriptLength := VarSetCapacity(nScript, (StrLen(script)+1)*(A_IsUnicode?2:1), 0)
,StrPut(script,&nScript)
; Reserve memory in remote process where our script will be saved
if !pBufferRemote := DllCall("VirtualAllocEx", "Ptr", hProc, "Ptr", 0, "PTR", nScriptLength, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
return DllCall("MessageBox","PTR",0,"Str","Could not reseve memory for process.","STR","Error","UInt",0)
,DllCall("CloseHandle", "PTR", hProc)
; Write script to remote process memory
DllCall("WriteProcessMemory", "Ptr", hProc, "Ptr", pBufferRemote, "Ptr", &nScript, "PTR", nScriptLength, "Ptr", 0)
; Start execution of code
hThread := DllCall("CreateRemoteThread", "PTR", hProc, "PTR", 0, "PTR", 0, "PTR", PID.ahkExec, "PTR", pBufferRemote, "UInt", 0, "PTR", 0,"PTR")
if !hThread
{
DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nScriptLength,MEM_RELEASE)
,DllCall("CloseHandle", "PTR", hProc)
return DllCall("MessageBox","PTR",0,"Str","Could not execute script in remote process.","STR","Error","UInt",0)
}
; Wait for thread to finish
DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)
; Get Exit code returned by ahkExec (1 = script could be executed / 0 = script could not be executed)
DllCall("GetExitCodeThread", "PTR", hThread, "UIntP", lpExitCode)
if !lpExitCode
return DllCall("MessageBox","PTR",0,"Str","Could not execute script in remote process.","STR","Error","UInt",0)
DllCall("CloseHandle", "PTR", hThread)
,DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nScriptLength,MEM_RELEASE)
,DllCall("CloseHandle", "PTR", hProc)
return
}
else if !hDll:=DllCall("LoadLibrary","Str",dll,"PTR")
return DllCall("MessageBox","PTR",0,"Str","Could not find " dll " library.","STR","Error","UInt",0),DllCall("CloseHandle", "PTR", hProc)
else
{
hProc := DllCall("OpenProcess","UInt", PROCESS_ALL_ACCESS, "Int",0,"UInt", DllCall("GetCurrentProcessId"),"PTR")
DllCall("GetModuleFileName","PTR",hDll,"PTR",&ModuleName,"UInt",MAX_PATH)
DllCall("CloseHandle","PTR",hProc)
}
; Open Process to PID
hProc := DllCall("OpenProcess", "UInt", PROCESS_ALL_ACCESS, "Int",0, "UInt", PID,"PTR")
if !hProc
return DllCall("MessageBox","PTR",0,"Str","Could not open process for PID: " PID,"STR","Error","UInt",0)
; Reserve some memory and write dll path (ANSI)
nDirLength := VarSetCapacity(nDir, StrLen(dll)+1, 0)
,StrPut(dll,&nDir,"CP0")
; Reserve memory in remote process
if !pBufferRemote := DllCall("VirtualAllocEx", "Ptr", hProc, "Ptr", 0, "PTR", nDirLength, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
return DllCall("MessageBox","PTR",0,"Str","Could not reseve memory for process.","STR","Error","UInt",0),DllCall("CloseHandle", "PTR", hProc)
; Write dll path to remote process memory
DllCall("WriteProcessMemory", "Ptr", hProc, "Ptr", pBufferRemote, "Ptr", &nDir, "PTR", nDirLength, "Ptr", 0)
; Start new thread loading our dll
hThread:=DllCall("CreateRemoteThread","PTR",hProc,"PTR",0,"PTR",0,"PTR",LoadLibraryA,"PTR",pBufferRemote,"UInt",0,"PTR",0,"PTR")
if !hThread
{
DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nDirLength,"Uint",MEM_RELEASE)
,DllCall("CloseHandle", "PTR", hProc)
return DllCall("MessageBox","PTR",0,"Str","Could not load " dll " in remote process.","STR","Error","UInt",0)
}
; Wait for thread to finish
DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)
; Get Exit code returned by thread (HMODULE for our dll)
DllCall("GetExitCodeThread", "PTR", hThread, "UInt*", hModule)
; Close Thread
DllCall("CloseHandle", "PTR", hThread)
if (A_PtrSize=8)
{ ; use different method to retrieve base address because GetExitCodeThread returns DWORD only
hModule:=0,VarSetCapacity(me32, (A_PtrSize=8?48:32)+(A_IsUnicode?1032:516), 0) ;W:1080:1064, A:564:548
; Take a snapshot of all modules in the specified process.
hModuleSnap := DllCall("CreateToolhelp32Snapshot","UInt", TH32CS_SNAPMODULE,"UInt", PID, "PTR" )
if ( hModuleSnap != INVALID_HANDLE_VALUE )
{
; reset hModule and set the size of the structure before using it.
NumPut((A_PtrSize=8?48:32)+(A_IsUnicode?1032:516), &me32, 0, "UInt") ;dwSize ;W:1080:1064, A:564:548
; Retrieve information about the first module,
; and exit if unsuccessful
if ( !DllCall("Module32First" (A_IsUnicode?"W":""),"PTR", hModuleSnap,"PTR", &me32 ) )
{
; Free memory used for passing dll path to remote thread
DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nDirLength,MEM_RELEASE)
,DllCall("CloseHandle","PTR", hModuleSnap ) ; Must clean up the snapshot object!
return false
}
; Now walk the module list of the process,and display information about each module
while(A_Index=1 || DllCall("Module32Next" (A_IsUnicode?"W":""),"PTR",hModuleSnap,"PTR", &me32 ) )
if (StrGet(&me32+(A_PtrSize=8?48:32)+(A_IsUnicode?512:256))=dll) ;szExePath ;W:560:544, A:304:288
{
hModule := NumGet(me32, A_PtrSize=8?40:28, "Ptr") ;hModule
break
}
DllCall("CloseHandle","PTR",hModuleSnap) ; clean up
}
}
hDll:=DllCall("LoadLibrary","Str",dll,"PTR")
; Calculate pointer to ahkdll and ahkExec functions
ahktextdll:=hModule+DllCall("GetProcAddress","PTR",hDll,"AStr","ahktextdll","PTR")-hDll
ahkExec:=hModule+DllCall("GetProcAddress","PTR",hDll,"AStr","ahkExec","PTR")-hDll
ahkTerminate:=hModule+DllCall("GetProcAddress","PTR",hDll,"AStr","ahkTerminate","PTR")-hDll
if script
{
nScriptLength := VarSetCapacity(nScript, (StrLen(script)+1)*(A_IsUnicode?2:1), 0)
,StrPut(script,&nScript)
; Reserve memory in remote process where our script will be saved
if !pBufferScript := DllCall("VirtualAllocEx", "Ptr", hProc, "Ptr", 0, "PTR", nScriptLength, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
return DllCall("MessageBox","PTR",0,"Str","Could not reseve memory for process.","STR","Error","UInt",0)
,DllCall("CloseHandle", "PTR", hProc)
; Write script to remote process memory
DllCall("WriteProcessMemory", "Ptr", hProc, "Ptr", pBufferScript, "Ptr", &nScript, "PTR", nScriptLength, "Ptr", 0)
}
else
pBufferScript:=0
; Run ahkdll function in remote thread
hThread := DllCall("CreateRemoteThread","PTR",hProc,"PTR",0,"PTR",0,"PTR",ahktextdll,"PTR",pBufferScript,"PTR",0,"UInt",0,"PTR")
if !hThread
{ ; could not start ahkdll in remote process
; Free memory used for passing dll path to remote thread
DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nDirLength,MEM_RELEASE)
DllCall("CloseHandle", "PTR", hProc)
return DllCall("MessageBox","PTR",0,"Str","Could not start ahkdll in remote process","STR","Error","UInt",0)
}
DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)
DllCall("GetExitCodeThread", "PTR", hThread, "UIntP", lpExitCode)
; Release memory and handles
DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nDirLength,MEM_RELEASE)
DllCall("CloseHandle", "PTR", hThread)
DllCall("CloseHandle", "PTR", hProc)
if !lpExitCode ; thread could not be created.
return DllCall("MessageBox","PTR",0,"Str","Could not create a thread in remote process","STR","Error","UInt",0)
return {PID:PID,hModule:hModule,ahkExec:ahkExec,ahkTerminate:ahkTerminate,base:base}
}
;==================================================
notepad get/set path (get/set text file path) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=30050
- Using Spek as the test case was inspired by this thread:
Open selected file in SPEK - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=34754
Queries:
- Does it work on x64 processes?
- Does it work to drag-and-drop multiple files?
- Why doesn't this script, that uses a different approach, work?
[COM] Help with the IDropSource and IDropTarget interfaces - Page 3 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=8700&p=187406#p187406
- I had to use BindToObject to get a pointer to an IShellFolder interface, and specify file names without dirs to make this script work in Windows 7. According to qwerty12 this was not necessary on Windows 10, you could just specify Desktop as the folder and use full paths.
Links:
[some utilities that return a value for OleDropTargetInterface]
[COM] Help with the IDropSource and IDropTarget interfaces - Page 3 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=8700&p=165994#p165994
[clue: GetProp and OleDropTargetInterface]
Sending WM_DROPFILES with C++ and WinAPI to third-party application - Stack Overflow
https://stackoverflow.com/questions/22271857/sending-wm-dropfiles-with-c-and-winapi-to-third-party-application
[clue: 'When you call SomeFolder.GetUIObjectOf items MUST be childs of SomeFolder', which led me to using BindToObject to get a pointer to a folder interface]
delphi - How to make my file DropSouce be accepted by all targets that works with files? - Stack Overflow
https://stackoverflow.com/questions/31356071/how-to-make-my-file-dropsouce-be-accepted-by-all-targets-that-works-with-files
IShellFolder::BindToObject | Microsoft Docs
https://docs.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nf-shobjidl_core-ishellfolder-bindtoobject
shell - BindToObject fails when binding to the desktop - Stack Overflow
https://stackoverflow.com/questions/25351407/bindtoobject-fails-when-binding-to-the-desktop