1

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

На рабочем столе лежит файл 1.txt. Открыт блокнот.
При запуске кода пишет "не удается найти указанный файл"
После строки

VarSetCapacity( DROPFILES,20,32)

В переменной DROPFILES появляются лишние символы. Так и должно быть?

SetTitleMatchMode, 2

DropFilesA(A_Desktop "\1.txt", "ahk_class Notepad")

DropFilesA( FileList, wTitle="", Ctrl="", X=0, Y=0, NCA=0 ) {
 StringReplace, FileList, FileList, `r`n, `n , All
 VarSetCapacity( DROPFILES,20,32)
 DROPFILES.=FileList "`n`n"
 nSize:=StrLen(DROPFILES)
 StringReplace, DROPFILES,DROPFILES, `n,`n, UseErrorLevel
 Loop %ErrorLevel%
   NumPut( 0, DROPFILES, InStr(DROPFILES,"`n",0,0)-1, "Char" )
 pDP:=&DROPFILES
 NumPut(20,pDP+0)
 NumPut(X,pDP+4)
 NumPut(Y,pDP+8)
 NumPut(NCA,pDP+12)
 NumPut(0,pDP+16)
 hDrop := DllCall( "GlobalAlloc", UInt,0x42, UInt,nSize )
 pData := DllCall( "GlobalLock", UInt,hDrop )
 DllCall( "RtlMoveMemory", UInt,pData, UInt,pDP, UInt,nSize )
 DllCall( "GlobalUnlock", UInt,hDrop )
 PostMessage, 0x233, hDrop, 0, %Ctrl%, %wTitle% ; WM_DROPFILES := 0x233
}

2

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

С кодом подробно не разбирался, но после строки

VarSetCapacity( DROPFILES,20,32)

в каждый байт переменной DROPFILES помещается число 32 (0x20), которое соответствует пробелу в ANSI либо знаку "†" в юникоде (0x2020). Отсюда "лишние" симолы.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

3 (изменено: InFlames, 2012-05-13 21:41:00)

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

Всё верно. Несколько символов † появляется. На офф. форуме, где я брал скрипт, писали, что он работает. Но у меня не получается получить результат.
Видимо этот код писался для AHK Basic и в переменной должны быть пробелы. Попробовал запустить на AHK Basic, скрипт заработал.
К сожалению знаний не хватает, что бы понять, как поправить для AHK_L.
Видимо дело в вызываемых функциях.
И как записать пробелы в переменную Dropfiles вместо крестов?

4

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

Для начала, как конкретно он должен работать?
...
А, уже понял, посмотрю.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

5

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

Примерно так:

DropFilesA(A_Desktop "\1.txt", "ahk_class Notepad")

DropFilesA( FileList, wTitle="", Ctrl="", X=0, Y=0, NCA=0 )
{
   offset := 20
   VarSetCapacity( DROPFILES, StrLen(FileList) + offset + 2)
   Loop, parse, FileList, `n, `r
   {
      StrPut(A_LoopField, &DROPFILES + offset, StrLen(A_LoopField), "CP0")
      offset += StrLen(A_LoopField) + 4
      NumPut(0, &DROPFILES + offset - 4, "UInt")   ; разделяем нулями пути
   }
   NumPut(0, &DROPFILES + offset, "UInt")   ; завершиться должно 2-мя нулями подряд
   nSize := offset + 4
   pDP:=&DROPFILES
   NumPut(20, pDP+0, "UInt")
   NumPut(X, pDP+4, "UInt")
   NumPut(Y, pDP+8, "UInt")
   NumPut(NCA, pDP+12, "UInt")
   NumPut(0, pDP+16, "UInt")
   hDrop := DllCall( "GlobalAlloc", UInt, 0x42, UInt, nSize, Ptr )
   pData := DllCall( "GlobalLock", Ptr, hDrop, Ptr )
   DllCall( "RtlMoveMemory", Ptr, pData, Ptr, pDP, UInt, nSize )
   DllCall( "GlobalUnlock", Ptr, hDrop )
   PostMessage, 0x233, hDrop, 0, %Ctrl%, %wTitle% ; WM_DROPFILES := 0x233
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

6 (изменено: Irbis, 2012-05-14 00:50:22)

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

AHK_L работает нормально, если не запускать скрипт, а скомпилировать через AHK2EXE  как ANSI версию. (Это про скрипт из поста #1)
Хотя это, наверное, и так понятно, что проблема в Unicode.

Upd: teadrinker как всегда на высоте http://resources.pokerstrategy.com/2011/10/16/applo.gif

7

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

Спасибо. Работает отлично.
Не совсем понял, почему 32 в юникоде это †? Номер 32 с 0x2020 никак не соотносится.

8

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

32 это 0x20, в unicode-версии AHK строки записываются в память в кодировке UTF-16, в которой на каждый символ приходится 2 байта, соответственно считывается 0x2020.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

9

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

Не могу понять в чём причина —

Gui +LastFound
DropFilesA("123`n234")
return

GuiDropFiles:
    MsgBox % A_GuiEvent
    ExitApp

более одного «файла» не удаётся передать.

10 (изменено: serzh82saratov, 2013-05-11 02:15:36)

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

creature.ws

DropFilesA("123``n234")

Upd:
Сорри, поспешил

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru
OS: Win7x64, AutoHotkey_L v1.1.30.03 (Unicode 32-bit). AhkSpy, Hotkey, ClockGui

11 (изменено: creature.ws, 2013-05-11 14:49:31)

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

Вопрос снят
Нужно было использовать один \0 в качестве разделителя в списке файлов.

12

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

creature.ws пишет:

Вопрос снят
Нужно было использовать один \0 в качестве разделителя в списке файлов.

А это как?

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru
OS: Win7x64, AutoHotkey_L v1.1.30.03 (Unicode 32-bit). AhkSpy, Hotkey, ClockGui

13

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

   VarSetCapacity( DROPFILES, StrLen(FileList) + offset + 2)
   Loop, parse, FileList, `n, `r
   {
      StrPut(A_LoopField, &DROPFILES + offset, StrLen(A_LoopField), "CP0")
      offset += StrLen(A_LoopField) + 4
      NumPut(0, &DROPFILES + offset - 4, "UInt")   ; разделяем нулями пути
   }

Так:

    VarSetCapacity(dropFiles, StrPut(fileList) + offset + 2, 0)

    loop parse, fileList, `n, `r
        StrPut(A_LoopField, &dropFiles + offset, StrLen(A_LoopField), "CP0")
        , offset += StrLen(A_LoopField) + 1

14

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

Работает. Раньше приходилось передавать по одному.
То есть функция в исправленном виде выглядит:


DropFilesA( FileList, wTitle="", Ctrl="", X=0, Y=0, NCA=0 )
{
    offset := 20
    VarSetCapacity(dropFiles, StrPut(fileList) + offset + 2, 0)
    loop parse, fileList, `n, `r
        StrPut(A_LoopField, &dropFiles + offset, StrLen(A_LoopField), "CP0")
        , offset += StrLen(A_LoopField) + 1
    NumPut(0, &DROPFILES + offset, "UInt")   ; завершиться должно 2-мя нулями подряд
    nSize := offset + 4
    pDP:=&DROPFILES
    NumPut(20, pDP+0, "UInt")
    NumPut(X, pDP+4, "UInt")
    NumPut(Y, pDP+8, "UInt")
    NumPut(NCA, pDP+12, "UInt")
    NumPut(0, pDP+16, "UInt")
    hDrop := DllCall( "GlobalAlloc", UInt, 0x42, UInt, nSize, Ptr )
    pData := DllCall( "GlobalLock", Ptr, hDrop, Ptr )
    DllCall( "RtlMoveMemory", Ptr, pData, Ptr, pDP, UInt, nSize )
    DllCall( "GlobalUnlock", Ptr, hDrop )
    PostMessage, 0x233, hDrop, 0, %Ctrl%, %wTitle% ; WM_DROPFILES := 0x233
}
По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru
OS: Win7x64, AutoHotkey_L v1.1.30.03 (Unicode 32-bit). AhkSpy, Hotkey, ClockGui

15 (изменено: creature.ws, 2013-05-11 15:40:02)

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

DropFiles(ByRef fileList, windowTitle := "", ctrl := "", x := 0, y := 0, NC := 0) {
    offset := 20
    VarSetCapacity(dropFiles, StrLen(fileList) + offset + 2, 0)
    pDropFiles := &dropFiles

    loop parse, fileList, `n, `r
        StrPut(A_LoopField, pDropFiles + offset, StrLen(A_LoopField), "CP0")
        , offset += StrLen(A_LoopField) + 1

    NumPut(20, pDropFiles + 0, "UInt")
    NumPut(x,  pDropFiles + 4, "UInt")
    NumPut(y,  pDropFiles + 8, "UInt")
    NumPut(NC, pDropFiles + 12, "UInt")
    NumPut(0,  pDropFiles + 16, "UInt")

    size := offset + 1
    hDrop := DllCall("GlobalAlloc", "UInt", 0x42, "UInt", size, "Ptr")
    pData := DllCall("GlobalLock", "Ptr", hDrop, "Ptr")
    DllCall("RtlMoveMemory", "Ptr", pData, "Ptr", pDropFiles, "UInt", size)
    DllCall("GlobalUnlock", "Ptr", hDrop)

    PostMessage 0x233, hDrop, 0, %ctrl%, %windowTitle%
}

Или так, но 3 байта, кажется, не значат много

16

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

Обновлённая версия с поддержкой юникода.

На окно - замечательно работает. А как перетянуть файлы не на окно, а на определённый контрол окна?
Есть утилитка EncryptCare, она принимает файлы только при перетаскивании на TListView1. Пытаюсь сделать так:
DropFiles( FileList, "ahk_exe EncryptCare.exe", "TListView1" )
Не работает.

17

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

Там же координаты есть. Попробуйте указать координаты внутри контрола, отличные от 0.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

18 (изменено: Aslan, 2018-09-15 20:14:22)

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

teadrinker пишет:

Там же координаты есть. Попробуйте указать координаты внутри контрола, отличные от 0.

Пробовал так:
DropFiles( FileList, "ahk_exe EncryptCare.exe", "TListView1", 100, 100 )
Пробовал указать координаты внутри всего окна:
DropFiles( FileList, "ahk_exe EncryptCare.exe",, 300, 300 )
Не срабатывает.

Пробовал предварительно ControlFocus, ControlClick делать, определять Hwnd контрола и передавать его... безрезультатно.

Многие программы умеют принимать файлы при перетаскивании на любое место окна. Но если для них указываю определённый контрол, то файлы тоже не принимаются.
Или эта возможность в функции вообще недоделана и не работает (или и не должна работать) или я что-то неправильно записываю?

19

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

Трудно сказать, не имея вашей программы.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

20

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

Aslan, посмотрите через spy++, что отправляется окну при перетаскивани.
Раз это ListView, то возможно отправляется LVM_INSERTITEM.

21 (изменено: Aslan, 2018-09-16 02:23:39)

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

teadrinker пишет:

Трудно сказать, не имея вашей программы.

Как я написал выше - безрезультатно пробовал передачу файлов на контролы разных программ (указываю wTitle - всё работает, указываю wTitle и Ctrl - не работает).

Подскажите по тем программам, которые у вас есть.
В какой-нибудь программе (ссылочку на неё или название) работает передача файлов скриптом на определённый контрол этой программы? И как выглядит правильная запись передачи файлов именно на контрол? (примеров с подобным использованием функции не нашёл, свои тщетные попытки записи выложил выше).

Malcev пишет:

Aslan, посмотрите через spy++, что отправляется окну при перетаскивани.
Раз это ListView, то возможно отправляется LVM_INSERTITEM.

Для разных программ генерируются разное во время отпускания перетаскиваемого файла. Именно такой записи в логах не встретил.
На всякий случай попробовал заменять в функции 0x233 на те коды сообщений, которые отправляются во время отпускания файла - не сработало.

22

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

Попробую.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

23 (изменено: teadrinker, 2018-09-16 03:35:35)

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

Функция у меня работает, хоть и составлена не совсем корректно. Мой вариант:

DropFiles(fileList, windowTitle := "", ctrl := "", x := 0, y := 0, NC := 0, isUnicodeFiles := 0)  {
   fileList := Trim( RegExReplace(fileList, "\R", "`n"), " `t`r`n" ), RegExReplace(fileList, "\R", "", count)
   VarSetCapacity(DROPFILES, size := 20 + StrLen(fileList) + count + 2, 0)
   NumPut(20, DROPFILES)
   NumPut(x, DROPFILES, 4)
   NumPut(y, DROPFILES, 8)
   NumPut(NC, DROPFILES, 12)
   NumPut(isUnicodeFiles, DROPFILES, 16)
   
   prevLen := 0
   Loop, parse, fileList, `n
      prevLen += StrPut( A_LoopField, &DROPFILES + 20 + prevLen + A_Index - 1, StrLen(A_LoopField), "CP0" )
   
   hDrop := DllCall("GlobalAlloc", UInt, 0x42, Ptr, size, Ptr)
   pData := DllCall("GlobalLock", Ptr, hDrop, Ptr)
   DllCall("RtlMoveMemory", Ptr, pData, Ptr, &DROPFILES, Ptr, size)
   DllCall("GlobalUnlock", Ptr, hDrop)
   PostMessage, WM_DROPFILES := 0x233, hDrop,, % ctrl, % windowTitle
}

Для проверки запустите сначала такой скрипт:

WM_DROPFILES := 0x233, WS_EX_ACCEPTFILES := 0x10

Gui, +AlwaysOnTop
Gui, Margin, 10, 10
Gui, Font, s12 q5, calibri
Gui, Add, Text, y70 h70 Border +E%WS_EX_ACCEPTFILES%, % "   ↓ Перетащите сюда файлы ↓   "
OnMessage(0x233, "WM_DROPFILES")
Gui, Show, y100, Drop Files Test
Return

GuiClose:
   ExitApp

WM_DROPFILES(hDrop)  {
   countFiles := DllCall("Shell32\DragQueryFile", Ptr, hDrop, UInt, 0xFFFFFFFF, Ptr, 0, UInt, 0)
   Loop % countFiles  {
      size := DllCall("Shell32\DragQueryFile", Ptr, hDrop, UInt, A_Index - 1, Ptr, 0, UInt, 0)
      VarSetCapacity(buff, size + 1, 0)
      DllCall("Shell32\DragQueryFile", Ptr, hDrop, UInt, A_Index - 1, Ptr, &buff, UInt, size + 1)
      fileNames .= (fileNames = "" ? "" : "`r`n") . StrGet(&buff, "CP0")
   }
   DllCall("Shell32\DragFinish", Ptr, hDrop)
   timer := Func("ShowFileNames").Bind(fileNames)
   SetTimer, % timer, -10
}

ShowFileNames(fileNames)  {
   MsgBox, % fileNames
}

В появившееся окно можно перетаскивать файлы вручную, если опускать их в выделенную область, в ответ будет появляться Message Box со списком файлов.
Не завершая первый скрипт, запустите такой:

fileList =
(
%A_WinDir%
%A_ProgramFiles%
%A_Temp%
)

DropFiles(fileList, "Drop Files Test", "Static1")

DropFiles(fileList, windowTitle := "", ctrl := "", x := 0, y := 0, NC := 0, isUnicodeFiles := 0)  {
   fileList := Trim( RegExReplace(fileList, "\R", "`n"), " `t`r`n" ), RegExReplace(fileList, "\R", "", count)
   VarSetCapacity(DROPFILES, size := 20 + StrLen(fileList) + count + 2, 0)
   NumPut(20, DROPFILES)
   NumPut(x, DROPFILES, 4)
   NumPut(y, DROPFILES, 8)
   NumPut(NC, DROPFILES, 12)
   NumPut(isUnicodeFiles, DROPFILES, 16)
   
   prevLen := 0
   Loop, parse, fileList, `n
      prevLen += StrPut( A_LoopField, &DROPFILES + 20 + prevLen + A_Index - 1, StrLen(A_LoopField), "CP0" )
   
   hDrop := DllCall("GlobalAlloc", UInt, 0x42, Ptr, size, Ptr)
   pData := DllCall("GlobalLock", Ptr, hDrop, Ptr)
   DllCall("RtlMoveMemory", Ptr, pData, Ptr, &DROPFILES, Ptr, size)
   DllCall("GlobalUnlock", Ptr, hDrop)
   PostMessage, WM_DROPFILES := 0x233, hDrop,, % ctrl, % windowTitle
}

Первый скрипт должен показать список указанных папок.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg

24

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

Aslan пишет:

Именно такой записи в логах не встретил.

А она может быть и не такой.
Смотрите по хексу.
https://www.geoffchappell.com/studies/w … /index.htm

Aslan пишет:

На всякий случай попробовал заменять в функции 0x233 на те коды сообщений

Так не получится, так как передаваемые структуры разные.

25

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

Спасибо за информацию, буду разбираться.
Похоже, всё сложнее, чем казалось, разные программы по разному принимают файлы.

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