26 (изменено: Malcev, 2018-09-18 12:21:41)

Re: AHK: Передача файла в приложение методом Drag&Drop

Да. Либо через WM_DROPFILES либо через IDropTarget.
https://docs.microsoft.com/en-us/window … droptarget
Посылание LVM_INSERTITEM тут не поможет, так как это будет просто изменение в гуи.
IDropTarget обсуждается тут и jeeswg вроде выложит наработки через пару дней.
https://autohotkey.com/boards/viewtopic … amp;t=8700

27 (изменено: Malcev, 2018-09-28 14:43:49)

Re: AHK: Передача файла в приложение методом Drag&Drop

Тема, дествительно, очень непростая.
qwerty12 смог добиться посылать файлы с рабочего стола.
С путями файлы не посылаются, он предполагает, что возможно причина в этом:
https://stackoverflow.com/questions/313 … 8#31362098
Сам он уже не пользуется виндой и shell COM интерфейсы особо не изучал, поэтому поправить не может.
Для теста скачиваем программу spek:
https://github.com/withmorten/spek-alternative/releases

AtoHotkeyMini.dll
https://github.com/HotKeyIt/ahkdll-v1-r … ter/Win32w

AutoHotkeyU32 loader.ahk

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

Inject()

Inject()
{
	WinGet, nPid, PID, Spek - ahk_class wxWindowNR ahk_exe spek.exe
	if (nPid)
		InjectAhkDllMod(nPid, A_ScriptDir . "\AutoHotkeyMini.dll", "#Include " . A_ScriptDir . "\hook.ahk")
}

;==================================================

;[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)
;(also added a PreventUnload parameter)

InjectAhkDllMod(PID, dll:="AutoHotkey.dll", script:=0, PreventUnload:=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 PreventUnload
		base:={__Call:"InjectAhkDll"}

	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
	if !PreventUnload
		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 PreventUnload && hDll
		DllCall(FreeLibrary, "Ptr", hDll), hDll := 0

	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)

	if PreventUnload
		return true
	return {PID:PID,hModule:hModule,ahkExec:ahkExec,ahkTerminate:ahkTerminate,base:base}
}

;==================================================

hook.ahk

;note: qwerty12 called it WM_JEEDND (not jeeswg)

#NoEnv
#Persistent
#NoTrayIcon
SetBatchLines, -1
;DetectHiddenWindows On

main(), return

main()
{
	global WM_JEEDND := DllCall("RegisterWindowMessage", "Str", "WM_JEEDND", "UInt"), msgHookCb, msgHook, hWnd

	if ((msgHookCb := RegisterCallback("CallWndProc", "Fast"))) {
		OnExit("AtExit")
		if ((hWnd := WinExist("Spek - ahk_class wxWindowNR ahk_exe spek.exe ahk_pid " . DllCall("GetCurrentProcessId", "UInt"))) && (tId := DllCall("GetWindowThreadProcessId", "Ptr", hWnd, "Ptr", 0, "UInt")))
			msgHook := DllCall("SetWindowsHookEx", "Int", WH_CALLWNDPROC := 4, "Ptr", msgHookCb, "Ptr", 0, "UInt", tId, "Ptr")
	}
}

AtExit()
{
	global msgHook, msgHookCb
	OnExit(A_ThisFunc, 0)

	if (msgHook)
		DllCall("UnhookWindowsHookEx", "Ptr", msgHook), msgHook := 0

	if (msgHookCb)
		DllCall("GlobalFree", "Ptr", msgHookCb, "Ptr"), msgHookCb := 0

	return 0
}

CallWndProc(nCode, wParam, lParam)
{
	Critical 999
	global msgHook, WM_JEEDND, hWnd
	PassToChain := True

	if (nCode >= 0) { ; HC_ACTION
		;msgLparam := NumGet(lParam+0,, "Ptr")
		msg := NumGet(lParam+0, A_PtrSize * 2, "UInt")
		msghWnd := NumGet(lParam+0, A_PtrSize * 3, "Ptr")

		if (msg == WM_JEEDND && msghWnd == hWnd) {
			PassToChain := False
			msgWparam := NumGet(lParam+0, A_PtrSize, "Ptr")
			if (msgWparam)
				DoDnd([StrGet(msgWparam,, "UTF-16")])
		}
	}

	return PassToChain ? DllCall("CallNextHookEx", "Ptr", msgHook, "Int", nCode, "Ptr", wParam, "Ptr", lParam, "Ptr") : 0
}

DoDnd(filenames)
{
	; qwerty12
	global hWnd
	static IID_IDataObject
	ret := False

	if (!IsObject(filenames) || !filenames.MaxIndex() || !filenames[1])
		return ret

	if (!(pUnk := DllCall("GetProp", "Ptr", hWnd, "Str", "OleDropTargetInterface", "Ptr")))
		return ret

	if (!VarSetCapacity(IID_IDataObject))
		VarSetCapacity(IID_IDataObject, 16)
		,DllCall("ole32\CLSIDFromString", "WStr", "{0000010e-0000-0000-C000-000000000046}", "Ptr", &IID_IDataObject)

	DllCall("shell32\SHGetDesktopFolder", "Ptr*", pDesktop)
	if (pDesktop) {
		pDropTarget := ComObjQuery(pUnk, "{00000122-0000-0000-C000-000000000046}")

		VarSetCapacity(pidl_list, filenames.MaxIndex() * A_PtrSize)
		filenameCount := 0, ParseDisplayName := NumGet(NumGet(pDesktop+0)+3*A_PtrSize)
		for _, filename in filenames
			filenameCount += DllCall(ParseDisplayName, "Ptr", pDesktop, "Ptr", 0, Ptr, 0, "WStr", filename, "Ptr", 0, "Ptr", &pidl_list+(filenameCount * A_PtrSize), "Ptr", 0) >= 0x00
		if (filenameCount) {
			DllCall(NumGet(NumGet(pDesktop+0)+10*A_PtrSize), "Ptr", pDesktop, "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)
			}
		}

		for _, obj in [pDropTarget, pDesktop]
			ObjRelease(obj)
	}

	return ret
}

test.ahk

;notes from qwerty12:
;•This "drags" one file only. Spek isn't exactly expecting more than one file to be dragged to it. DoDnd(filenames) is multi-filename ready, you need to modify the script sending the filenames with a suitable scheme (multiple files delimited by "|") and the hook function receiving the files to reconstruct a suitable list (StrSplit("|",...). Saying that, I have no idea if this actually works for multiple files.
;•Allocating and freeing the filename string is handled by the script sending the message only. This removes any ambiguity as to what should free the string
;•You aren't notified if the DnD operation succeeded - the hook allows for no way for the return value to be set, DoDnD's ret value is for show. SendMessage is used to try and make sure the filename string isn't freed too early. Depending on how many filenames you send, SendMessage's default timeout of 5 seconds might be too low
;•None of just me's code was used, so that's one less dependency. The shell already has a suitable IDataObject implementation.

#NoTrayIcon
#NoEnv

WM_JEEDND := DllCall("RegisterWindowMessage", "Str", "WM_JEEDND", "UInt")
; filenames := A_Desktop "\music.mp3" ; don`t work
filenames := "music.mp3"

hWnd := WinExist("Spek - ahk_class wxWindowNR ahk_exe spek.exe")
WinGet, nPid, PID, % "ahk_id " hWnd

if (!(hProcess := DllCall("OpenProcess", "UInt", PROCESS_QUERY_INFORMATION := 0x0400 | PROCESS_VM_OPERATION := 0x0008 | PROCESS_VM_READ := 0x0010 | PROCESS_VM_WRITE := 0x0020, "Int", False, "UInt", nPid, "Ptr")))
	throw

if (!(pRemoteFilenames := DllCall("VirtualAllocEx", "Ptr", hProcess, "Ptr", 0, "Ptr", (StrLen(filenames) + 1) * 2, "UInt", MEM_COMMIT := 0x00001000, "UInt", PAGE_READWRITE := 0x04, "Ptr")))
	throw

if (!DllCall("WriteProcessMemory", "Ptr", hProcess, "Ptr", pRemoteFilenames, "Ptr", &filenames, "Ptr", (StrLen(filenames) + 1) * 2, "Ptr", 0)) ; the hook assumes UTF-16 filenames. Bear that in mind
	throw

SendMessage, % WM_JEEDND, % pRemoteFilenames,,, % "ahk_id " hWnd

DllCall("VirtualFreeEx", "Ptr", hProcess, "Ptr", pRemoteFilenames, "Ptr", 0, "UInt", MEM_RELEASE := 0x8000)
DllCall("CloseHandle", "Ptr", hProcess)

В test.ahk изменяем filenames := "music.mp3" на свой mp3 находящийся на десктопе.
Запускаем spek.exe, потом AutoHotkeyU32 loader.ahk, потом test.ahk.

28

Re: AHK: Передача файла в приложение методом Drag&Drop

Рабочий код:
https://autohotkey.com/boards/viewtopic … mp;t=56720