Что нового

Вернуть текст из WM_COPYDATA

Webarion

Осваивающий
Сообщения
143
Репутация
24
Друзья, прошу помощи. Сейчас получается вернуть число, но нужно, чтобы возвращался произвольный текст в $Ret

Код:
$sData = 'Отправляемый текст'
$tMsg = DllStructCreate('char[' & StringLen($sData) + 1 & ']')
DllStructSetData($tMsg, 1, $sData)
$tCOPYDATA = DllStructCreate('dword;dword;ptr')
DllStructSetData($tCOPYDATA, 2, StringLen($sData) + 1)
DllStructSetData($tCOPYDATA, 3, DllStructGetPtr($tMsg))
$Ret = DllCall('user32.dll', 'lparam', 'SendMessage', 'hwnd', $hWnd, 'int', $WM_COPYDATA, 'wparam', 0, 'lparam', DllStructGetPtr($tCOPYDATA))

ConsoleWrite($Ret[0] & @CRLF)


Код:
GUIRegisterMsg($WM_COPYDATA, '_WM_COPYDATA')

Func _WM_COPYDATA($hWnd, $msgID, $wParam, $lParam)
	$tCOPYDATA = DllStructCreate('dword;dword;ptr', $lParam)
	$tMsg = DllStructCreate('char[' & DllStructGetData($tCOPYDATA, 2) & ']', DllStructGetData($tCOPYDATA, 3))
	$sText = DllStructGetData($tMsg, 1); получаем отправленный текст
	; выполняем операции
	Return 'Возвращаем произвольный текст' ; если здесь указать например Return 123, то это работает, с произвольным текстом не получается
EndFunc
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Я помню что есть возможность такая, как то нужно возвращать указатель на структуру с нужным текстом, а потом по нему уж доставать его.

Есть ещё такой вариант:

Код:
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

$hGUI = GUICreate('')
GUIRegisterMsg($WM_COPYDATA, '_WM_COPYDATA')
GUISetState()

$sData = 'Отправляемый текст'
$tMsg = DllStructCreate('wchar[' & StringLen($sData) + 1 & ']')
DllStructSetData($tMsg, 1, $sData)
$tCOPYDATA = DllStructCreate('dword;dword;ptr')
DllStructSetData($tCOPYDATA, 2, StringLen($sData) + 1)
DllStructSetData($tCOPYDATA, 3, DllStructGetPtr($tMsg))
$Ret = DllCall('user32.dll', 'lparam', 'SendMessageW', 'hwnd', $hGUI, 'int', $WM_COPYDATA, 'wparam', 0, 'lparam', DllStructGetPtr($tCOPYDATA))

If $Ret[0] = 123 Then
	Opt('WinTitleMatchMode', 3)
	$sRet = ControlGetText('[CLASS:AutoIt v3;TITLE:~MyTiTle~]', '', 'Edit1')
	ConsoleWrite('Ret: ' & $sRet & @CRLF)
EndIf

While 1
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE
			Exit
	EndSwitch
WEnd

Func _WM_COPYDATA($hWnd, $msgID, $wParam, $lParam)
    $tCOPYDATA = DllStructCreate('dword;dword;ptr', $lParam)
    $tMsg = DllStructCreate('wchar[' & DllStructGetData($tCOPYDATA, 2) & ']', DllStructGetData($tCOPYDATA, 3))
    $sText = DllStructGetData($tMsg, 1); получаем отправленный текст
    ; выполняем операции
	
	ConsoleWrite('Sent: ' & $sText & @CRLF)
	
	Opt('WinTitleMatchMode', 3)
	AutoItWinSetTitle('~MyTiTle~')
	ControlSetText('[CLASS:AutoIt v3;TITLE:~MyTiTle~]', '', 'Edit1', 'Возвращаем произвольный текст')
	
    Return 123
EndFunc
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Вот так вроде можно:

Код:
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPIEx.au3>

$hGUI = GUICreate('')
GUIRegisterMsg($WM_COPYDATA, '_WM_COPYDATA')
GUISetState()

$sData = 'Отправляемый текст'
$tMsg = DllStructCreate('wchar[' & StringLen($sData) + 1 & ']')
DllStructSetData($tMsg, 1, $sData)
$tCOPYDATA = DllStructCreate('dword;dword;ptr')
DllStructSetData($tCOPYDATA, 2, StringLen($sData) + 1)
DllStructSetData($tCOPYDATA, 3, DllStructGetPtr($tMsg))
$Ret = DllCall('user32.dll', 'lparam', 'SendMessageW', 'hwnd', $hGUI, 'int', $WM_COPYDATA, 'wparam', 0, 'lparam', DllStructGetPtr($tCOPYDATA))

If $Ret[0] Then
    $tStruct = _WinAPI_ReadProcessMemoryEx(@AutoItPID, $Ret[0], 'wchar[100]')
	ConsoleWrite(DllStructGetData($tStruct, 1) & @CRLF)
EndIf

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
    EndSwitch
WEnd

Func _WM_COPYDATA($hWnd, $msgID, $wParam, $lParam)
    $tCOPYDATA = DllStructCreate('dword;dword;ptr', $lParam)
    $tMsg = DllStructCreate('wchar[' & DllStructGetData($tCOPYDATA, 2) & ']', DllStructGetData($tCOPYDATA, 3))
    $sText = DllStructGetData($tMsg, 1); получаем отправленный текст
    ; выполняем операции
    
	$sSend = 'Возвращаем произвольный текст'
	$tStruct = DllStructCreate('wchar[100]')
	
	DllStructSetData($tStruct, 1, $sSend)
	$pPointer = DllStructGetPtr($tStruct)
    
    Return $pPointer
EndFunc

Func _WinAPI_ReadProcessMemoryEx($iProcessID, $pPointer, $sStructTag)
	Local $iSYNCHRONIZE = (0x00100000), $iSTANDARD_RIGHTS_REQUIRED = (0x000F0000)
	Local $iPROCESS_ALL_ACCESS = ($iSTANDARD_RIGHTS_REQUIRED + $iSYNCHRONIZE + 0xFFF)
	Local $hProcess, $Struct, $StructPtr, $StructSize, $Read
	
	$hProcess = _WinAPI_OpenProcess($iPROCESS_ALL_ACCESS, False, $iProcessID)
	If @error Then Return SetError(@error, 1, $Struct)
	
	$Struct = DllStructCreate($sStructTag)
	$StructSize = DllStructGetSize($Struct)
	$StructPtr = DllStructGetPtr($Struct)
	
	_WinAPI_ReadProcessMemory($hProcess, $pPointer, $StructPtr, $StructSize, $Read)
	_WinAPI_CloseHandle($hProcess)
	
	Return SetError(@error, $Read, $Struct)
EndFunc
 
Автор
W

Webarion

Осваивающий
Сообщения
143
Репутация
24
Спасибо CreatoR! Я пытался вернуть указатель, правда плохо понимаю как работают структуры между процессами, поэтому пока не получается. В идеале, конечно хотелось бы в ответе, правильно вернуть указатель на структуру и потом её прочитать.
Второй пример интересен, не работает в одном скрипте, но в разных работает. Немного подправил, так почти то что надо:
Код:
$tStruct = _WinAPI_ReadProcessMemoryEx(WinGetProcess($sSendProcessName), $Ret[0], 'wchar[100]'); $sSendProcessName - заголовок формы в процессе


Но почему-то возвращает неполный текст, первый символ отрезан:
Код:
?озвращаем произвольный текст



Добавлено:
Сообщение автоматически объединено:

CreatoR сказал(а):
Вот так вроде можно:

Код:
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPIEx.au3>

$hGUI = GUICreate('')
GUIRegisterMsg($WM_COPYDATA, '_WM_COPYDATA')
GUISetState()

$sData = 'Отправляемый текст'
$tMsg = DllStructCreate('wchar[' & StringLen($sData) + 1 & ']')
DllStructSetData($tMsg, 1, $sData)
$tCOPYDATA = DllStructCreate('dword;dword;ptr')
DllStructSetData($tCOPYDATA, 2, StringLen($sData) + 1)
DllStructSetData($tCOPYDATA, 3, DllStructGetPtr($tMsg))
$Ret = DllCall('user32.dll', 'lparam', 'SendMessageW', 'hwnd', $hGUI, 'int', $WM_COPYDATA, 'wparam', 0, 'lparam', DllStructGetPtr($tCOPYDATA))

If $Ret[0] Then
    $tStruct = _WinAPI_ReadProcessMemoryEx(@AutoItPID, $Ret[0], 'wchar[100]')
	ConsoleWrite(DllStructGetData($tStruct, 1) & @CRLF)
EndIf

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
    EndSwitch
WEnd

Func _WM_COPYDATA($hWnd, $msgID, $wParam, $lParam)
    $tCOPYDATA = DllStructCreate('dword;dword;ptr', $lParam)
    $tMsg = DllStructCreate('wchar[' & DllStructGetData($tCOPYDATA, 2) & ']', DllStructGetData($tCOPYDATA, 3))
    $sText = DllStructGetData($tMsg, 1); получаем отправленный текст
    ; выполняем операции
    
	$sSend = 'Возвращаем произвольный текст'
	$tStruct = DllStructCreate('wchar[100]')
	
	DllStructSetData($tStruct, 1, $sSend)
	$pPointer = DllStructGetPtr($tStruct)
    
    Return $pPointer
EndFunc

Func _WinAPI_ReadProcessMemoryEx($iProcessID, $pPointer, $sStructTag)
	Local $iSYNCHRONIZE = (0x00100000), $iSTANDARD_RIGHTS_REQUIRED = (0x000F0000)
	Local $iPROCESS_ALL_ACCESS = ($iSTANDARD_RIGHTS_REQUIRED + $iSYNCHRONIZE + 0xFFF)
	Local $hProcess, $Struct, $StructPtr, $StructSize, $Read
	
	$hProcess = _WinAPI_OpenProcess($iPROCESS_ALL_ACCESS, False, $iProcessID)
	If @error Then Return SetError(@error, 1, $Struct)
	
	$Struct = DllStructCreate($sStructTag)
	$StructSize = DllStructGetSize($Struct)
	$StructPtr = DllStructGetPtr($Struct)
	
	_WinAPI_ReadProcessMemory($hProcess, $pPointer, $StructPtr, $StructSize, $Read)
	_WinAPI_CloseHandle($hProcess)
	
	Return SetError(@error, $Read, $Struct)
EndFunc

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

Webarion

Осваивающий
Сообщения
143
Репутация
24
InnI сказал(а):
Подробное описание и готовая функция: Ответ #10

InnI, там нет нужного мне возвращения. Мне нужно вернуть строку неизвестной длины, в том числе большой текст уже из WM_COPYDATA. В примере по ссылке это $aResult[0] первого скрипта.
CreatoR дал два примера, первый работает, второй мне нравится больше, но пока я не могу понять как с помощью структур, получить произвольный текст(не передать, а уже получить после передачи). Разговор между процессами типа такой: верни мне этот список <> на тебе этот список.
 

Cytrus

Новичок
Сообщения
110
Репутация
3
У меня тоже, второй пример не работает.

_WinAPI_ReadProcessMemoryEx - вроде ошибок не возвращает, но он возвращает пустую строку.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Vanguger [?]
В примере по ссылке это $aResult[0] первого скрипта.
$aResult[0] содержит boolean, а не строку.

нужно вернуть строку неизвестной длины
См. по ссылке второй скрипт, который тоже заранее не знает размер текста, но читает его длину из поля cbData.
 
Автор
W

Webarion

Осваивающий
Сообщения
143
Репутация
24
InnI сказал(а):
См. по ссылке второй скрипт, который тоже заранее не знает размер текста, но читает его длину из поля cbData.

И всё равно не могу понять, как исходя из этого вернуть произвольный текст, возвращаются нули.

InnI, можешь пример привести рабочий?
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Vanguger [?]
пример привести рабочий
Я проверил примеры Yashied'а на AutoIt 3.3.14.5 - они рабочие. И это не просто примеры, а именно тот код, который должен быть в скриптах - один в передающем, другой - в принимающем.

Что вам конкретно непонятно? Один скрипт заполняет структуру данными, подсчитывает её размер и отправляет указатель другому скрипту (окну!) в сообщении WM_COPYDATA. Другой скрипт создаёт структуру по полученному в сообщении указателю и читает из неё данные.
 
Автор
W

Webarion

Осваивающий
Сообщения
143
Репутация
24
InnI сказал(а):
Что вам конкретно непонятно?

Один скрипт заполняет структуру данными, подсчитывает её размер и отправляет указатель другому скрипту (окну!) в сообщении WM_COPYDATA. Другой скрипт создаёт структуру по полученному в сообщении указателю и читает из неё данные.

Дополнительно нужно следующее: второй скрипт прочитав данные, выполняет некоторые операции и возвращает большой текст первому скрипту посредством Return. Таким образом в первом скрипте в $aResult[0] вместо состояний 1/0 нужно получить указатель на структуру, чтобы прочитать её из второго скрипта.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Vanguger [?]
возвращает большой текст первому скрипту посредством Return. Таким образом в первом скрипте в $aResult[0] вместо состояний 1/0 нужно получить указатель на структуру, чтобы прочитать её из второго скрипта
Нет, так не получится. В $aResult[0] находится результат выполнения системной функции SendMessage, которая к WM_COPYDATA никакого отношения не имеет. То, что вы хотите сделать, нужно сделать в обоих скриптах. Первый скрипт заполняет и отправляет. Второй скрипт получает, читает, выполняет, заполняет свою структуру, подсчитывает размер и отправляет сообщение первому скрипту, который читает данные из структуры. Другими словами, у вас в обоих скриптах должна быть и функция отправки (_SendMsg) и функция приёма (WM_COPYDATA).
 
Автор
W

Webarion

Осваивающий
Сообщения
143
Репутация
24
InnI сказал(а):
Другими словами, у вас в обоих скриптах должна быть и функция отправки (_SendMsg) и функция приёма (WM_COPYDATA).

Я это понимаю, было бы всё так просто, я бы и не спрашивал :smile: Такой способ у меня используется для других целей, когда мне не нужна строгая последовательность выполнения. Сейчас же мне нужно обратиться к процессу подобно как к функции $sReturn = _function($sData)

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

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Vanguger [?]
мне нужно обратиться к процессу подобно как к функции
Системная функция SendMessage так и работает: пока окно, которому отправлено сообщение не вернёт результат, эта функция будет ждать. Т.е. первый скрипт приостановит работу, пока в другом будет выполняться функция WM_COPYDATA.

С другой стороны, в структуре COPYDATA есть числовое поле dwData (второй параметр в функции _SendMsg). Следовательно, в одном скрипте нужно установить заранее известный код в это поле, а в другом прочитать этот код и понять, какая "функция" вернула данные.
 
Автор
W

Webarion

Осваивающий
Сообщения
143
Репутация
24
InnI сказал(а):
Системная функция SendMessage так и работает

На это и рассчитывал. В описании DllCall, есть return type, в документации написано - тип возвращаемого значения, и ниже, среди типов есть:
str - ANSI строка (минимум выделяется 65536 символа);
wstr - UNICODE широкий символ строки (минимум выделяется 65536 символа);
struct - Структура созданная функцией DllStructCreate();

Не совсем понятно что это значит. Сначала, я думал, что достаточно установить тип в str, чтобы вернуть текст, но это не прокатило :scratch:

Код:
Local $Ret = DllCall('user32.dll', 'str', 'SendMessageW', 'hwnd', $hWnd, 'int', $WM_COPYDATA, 'wparam', 0, 'lparam', DllStructGetPtr($tCOPYDATA))


При этом, в $Ret из _WM_COPYDATA, спокойно возвращается любое число, при установленном типе lparam

Код:
Local $Ret = DllCall('user32.dll', 'lparam', 'SendMessageW', 'hwnd', $hWnd, 'int', $WM_COPYDATA, 'wparam', 0, 'lparam', DllStructGetPtr($tCOPYDATA))
; здесь $Ret[0] будет равен 123456789

Func _WM_COPYDATA($hWnd, $msgID, $wParam, $lParam)
	Return 123456789
EndFunc


Пока что я над этим думаю, но от любых рабочих примеров не откажусь :smile:
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
InnI [?]
первый скрипт приостановит работу, пока в другом будет выполняться функция WM_COPYDATA.
Это можно обойти если использовать PostMessage.

Vanguger
Я так понял тебе нужна интеракция между скриптами? Есть библиотека Container, там можно передавать многие типы данных, включая массивы и указатели на структуру.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Vanguger [?]
Не совсем понятно что это значит
Каждая функция возвращает данные того типа, который указан программистом. И именно этот тип нужно указывать при вызове DllCall. В справке перечислены те типы, которые может обрабатывать AutoIt. Но это не значит, что их можно менять. Если вы замените bool на str, то AutoIt аварийно завершит работу, что у вас и происходит.

CreatoR [?]
Это можно обойти если использовать PostMessage
Нет, нельзя. Сообщение WM_COPYDATA сначала обрабатывает система, которая и копирует данные в адресное пространство процесса-приёмника. А затем окно приложения получает сообщение от системы и читает эти данные. Вот здесь пояснение "дяди Рихтера" (ответ №6 под спойлером).
 
Автор
W

Webarion

Осваивающий
Сообщения
143
Репутация
24
CreatoR сказал(а):
Я так понял тебе нужна интеракция между скриптами? Есть библиотека Container, там можно передавать многие типы данных, включая массивы и указатели на структуру.

Я тут несколько дней тестировал, всё что нашёл по интеракции, Container у меня в тестах вываливался с системной COM ошибкой. Мне достаточно передачи строковых параметров, поэтому, пока остановился на WM_COPYDATA.

Наверно нужно немного расписать, чего я добиваюсь. Допустим есть процесс ядро и есть процессы модули, с которыми должно быть взаимодействие. Есть два вида коммуникации, интерактивная и последовательная. В интерактивной, ядро не знает, когда появится запрос, это может зависеть как от вычислений модулей, так и от пользователя, здесь подходит получение данных с обоих сторон описанный InnI. Второй способ взаимодействия, последовательный. В какой-то момент необходимо опросить все модули подряд и получить от них строго организованную структуру, процедура опроса находится в цикле большой функции, в которой также происходит рекурсия. Здесь конечно можно пошаманить и заточить коммуникацию под первый способ, но это будет неудобно, гораздо лучше отправить запрос и получить ответ. Упрощённый пример:
Код:
Func _Module_Survey($sSource)
	Local $sModule, $sReturn, $sCollect = '', $aLines = StringSplit($sSource, @LF)
	For $i = 1 To $aLines[0]
		$sModule = _getParam($aLines[$i], 'module')
		If $sModule Then $sReturn = _Send($sModule)
		If $sReturn Then $sCollect &= _Module_Survey($sReturn)
	Next
	Return $sCollect
EndFunc


Здесь более удобно обрабатывать результат коннекта не в функции WM_COPYDATA, а иметь непосредственный запрос-ответ $sReturn = _Send($sModule)
Пока я остановился на первом примере CreatoR'а, он работает именно так.
 
Верх