Что нового

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

Webarion

Знающий
Сообщения
96
Репутация
7
Друзья, прошу помощи. Сейчас получается вернуть число, но нужно, чтобы возвращался произвольный текст в $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,610
Репутация
2,438
Я помню что есть возможность такая, как то нужно возвращать указатель на структуру с нужным текстом, а потом по нему уж доставать его.

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

Код:
#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,610
Репутация
2,438
Вот так вроде можно:

Код:
#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
 
Автор
Webarion

Webarion

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


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



Добавлено: [time]1554461751[/time]
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

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

Webarion

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

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

Cytrus

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

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

InnI

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

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

Webarion

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

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

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

InnI

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

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

Webarion

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

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

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

InnI

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

Webarion

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

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

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

InnI

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

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

Webarion

Знающий
Сообщения
96
Репутация
7
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,610
Репутация
2,438
InnI [?]
первый скрипт приостановит работу, пока в другом будет выполняться функция WM_COPYDATA.
Это можно обойти если использовать PostMessage.

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

InnI

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

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

Webarion

Знающий
Сообщения
96
Репутация
7
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'а, он работает именно так.
 
Верх