Что нового

Наиболее оптимальная связка приложения Delphi и скрипта Autoit

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
Есть маленькое Delphi-приложение, которое получает поток данных - таблицу(размерностью, например, 2х50) - из внешней программы. Необходимо передавать этот массив Autoit-скрипту с максимальной скоростью или иметь возможность быстро получить любую ячейку таблицы по запросу.

В принципе, пока вся эта таблица не нужна, поэтому обхожусь рисованием в Delphi нескольких кнопок со строками из таблицы и чтением этих кнопок из скрипта.
Но хотелось бы, чтобы окно Delphi-приложения было полностью скрыто, а в варианте с чтением кнопок так сделать не получиться.

При этом в Delphi я еще далеко чайник :wacko:

Может, кто-нибудь порекомендует возможный механизм передачи данных, наподобие связки Autoit и Excel, где можно просто запрашивать данные из нужной ячейки таблицы с большой скоростью?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Если вы в Delphi "еще далеко чайник", то это будет сложно. Самое простое и быстрое решение, это обеспечить обмен данными между двумя программами посредством WM_COPYDATA. Но здесь придется писать еще ответную часть в Delphi... Очень приближенно, это будет напоминать что-то типа своего собственного протокола связи. AutoIt посылает запрос Delphi на получение очередного (или всего сразу) элемента массива. В ответ на это, Delphi посылает в AutoIt значение требуемого элемента массива и т.д. В обоих программах нужно будет писать обработчики для WM_COPYDATA.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Вот простой пример. Поскольку Delphi у меня под рукой нет (да и подзабыл я его), напишу ответную часть на AutoIt. Перевести это на Delphi будет несложно.

Delphi.au3

Код:
#Include <Array.au3>
#Include <WindowsConstants.au3>

Global Const $RECEIVER_NAME = 'MzXq@Receiver#10'
Global Const $MSG_GETITEM = 0x0001
;~Global Const $MSG_...

Global Const $ERROR_INVALIDINDEX = 0xFFFF

Dim $Data[100]

For $i = 0 To 99
	$Data[$i] = Random(0, 1000, 1)
Next

GUICreate($RECEIVER_NAME)
GUIRegisterMsg($WM_COPYDATA, 'WM_COPYDATA')

_ArrayDisplay($Data)

;~While 1
;~	Sleep(1000)
;~WEnd

Func _SendMsg($hWnd, $iMsg, $iData, $hSender = 0)

	Local $tCOPYDATA = DllStructCreate('ulong_ptr;dword;ptr')
	Local $Ret

	DllStructSetData($tCOPYDATA, 1, BitOR(BitShift(BitAND($iMsg, 0xFFFF), -16), BitAND($iData, 0xFFFF)))
	DllStructSetData($tCOPYDATA, 2, 0)
	DllStructSetData($tCOPYDATA, 3, 0)
	$Ret = DllCall('user32.dll', 'lresult', 'SendMessage', 'hwnd', $hWnd, 'uint', $WM_COPYDATA, 'ptr', $hSender, 'ptr', DllStructGetPtr($tCOPYDATA))
	If (@error) Or (Not $Ret[0]) Then
		Return 0
	EndIf
	Return 1
EndFunc   ;==>_SendMsg

Func WM_COPYDATA($hWnd, $iMsg, $wParam, $lParam)

	Local $tCOPYDATA = DllStructCreate('ulong_ptr;dword;ptr', $lParam)
	Local $Msg = BitShift(DllStructGetData($tCOPYDATA, 1), 16)
	Local $Index

	Switch $Msg
		Case $MSG_GETITEM
			$Index = BitAND(DllStructGetData($tCOPYDATA, 1), 0xFFFF)
			If ($Index < 0) Or ($Index > UBound($Data) - 1) Then
				_SendMsg($wParam, 0, $ERROR_INVALIDINDEX)
			Else
				_SendMsg($wParam, 0, $Data[$Index])
			EndIf
;~		Case $MSG_...

	EndSwitch
	Return 1
EndFunc   ;==>WM_COPYDATA


Как сие чудо работает. Этот скрипт создает скрытое окно (т.к. WM_COPYDATA работает только с окнами) с уникальным именем ($RECEIVER_NAME) и ждет запросов со стороны клиента. В качестве примера я создал простой одномерный массив из 100 элементов ($Data[100]), содержащий целочисленные значения в диапазоне от 0 до 1000. Вообще, WM_COPYDATA позволяет передавать любые данные, в том числе и строки, но я не стал усложнять код и ограничился числовыми значениями (используется только первый параметр в структуре $tCOPYDATA, в котором в HIWORD передается код запроса, а в LOWORD - индекс элемента массива). В данном случае я реализовал только запрос на чтение одного элемента массива - $MSG_GETITEM.

Итак, программа-клиент посылает окну $RECEIVER_NAME сообщение WM_COPYDATA, содержащее код запроса $MSG_GETITEM (HIWORD) и индекс элемента массива (LOWORD), значение которого необходимо получить. В ответ на этот запрос, ответная часть отсылает обратно тому окну, которое прислало это сообщение, значение требуемого элемента массива (передается также в LOWORD). Если индекс выходит за пределы массива, то отсылается $ERROR_INVALIDINDEX. Вот и все. Остается только написать клиентскую часть в соответствии с вышеописанным "протоколом" обмена данными.

AutoIt.au3

Код:
#Include <EditConstants.au3>
#Include <GUIConstantsEx.au3>
#Include <WindowsConstants.au3>

Opt('WinTitleMatchMode', 3)
Opt('WinWaitDelay', 0)

Global Const $RECEIVER_NAME = 'MzXq@Receiver#10'
Global Const $MSG_GETITEM = 0x0001
;~Global Const $MSG_...

Global Const $ERROR_INVALIDINDEX = 0xFFFF

Global $Data

$hForm = GUICreate('MyGUI', 400, 400)
$Input = GUICtrlCreateInput('', 20, 20, 30, 19, $ES_NUMBER)
$Out = GUICtrlCreateInput('', 60, 20, 120, 19, $ES_READONLY)
$Button = GUICtrlCreateButton('Send', 20, 50, 60, 23)
GUIRegisterMsg($WM_COPYDATA, 'WM_COPYDATA')
GUISetState()

While 1
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE
			Exit
		Case $Button
			$hWnd = WinGetHandle($RECEIVER_NAME)
			If Not IshWnd($hWnd) Then
				; Ошибка: окно не найдено!
				GUICtrlSetData($Out, 'Server not found!')
			Else
				$Data = Default
				; Посылаем запрос на чтение элемента массива с индексом из $Input
				If Not _SendMsg($hWnd, $MSG_GETITEM, Number(GUICtrlRead($Input)), $hForm) Then
					; Ошибка: сообщение не отправлено!
					GUICtrlSetData($Out, 'Send message error!')
					ContinueLoop
				EndIf
				Switch $Data
					Case $ERROR_INVALIDINDEX
						GUICtrlSetData($Out, 'Invalid index!')
					Case Else
						GUICtrlSetData($Out, $Data)
				EndSwitch
			EndIf
	EndSwitch
WEnd

Func _SendMsg($hWnd, $iMsg, $iData, $hSender = 0)

	Local $tCOPYDATA = DllStructCreate('ulong_ptr;dword;ptr')
	Local $Ret

	DllStructSetData($tCOPYDATA, 1, BitOR(BitShift(BitAND($iMsg, 0xFFFF), -16), BitAND($iData, 0xFFFF)))
	DllStructSetData($tCOPYDATA, 2, 0)
	DllStructSetData($tCOPYDATA, 3, 0)
	$Ret = DllCall('user32.dll', 'lresult', 'SendMessage', 'hwnd', $hWnd, 'uint', $WM_COPYDATA, 'ptr', $hSender, 'ptr', DllStructGetPtr($tCOPYDATA))
	If (@error) Or (Not $Ret[0]) Then
		Return 0
	EndIf
	Return 1
EndFunc   ;==>_SendMsg

Func WM_COPYDATA($hWnd, $iMsg, $wParam, $lParam)

	Local $tCOPYDATA = DllStructCreate('ulong_ptr;dword;ptr', $lParam)

	Switch $hWnd
		Case $hForm
			$Data = BitAND(DllStructGetData($tCOPYDATA, 1), 0xFFFF)
			Return 1
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_COPYDATA


В первом поле Input введите требуемый индекс элемента массива и нажмите кнопку "Send". Программа (AutoIt.au3) отправит запрос $MSG_GETITEM серверу (Delphi.au3) на чтение элемента массива и будет ждать ответа. Если сервер запущен, то он сразу же пошлет в ответ на этот запрос сообщение, содержащее необходимое значение, и вы увидите его во втором поле Input.

:smile:

P.S

Функция _SendMsg() в обоих скриптах используется одна и таже.
 
Автор
W

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
Yashied
Во-первых, большое спасибо за саму идею, и, естественно, за масштабные примеры.

Сейчас потихоньку разбираюсь, нашел в сети два примера(видимо, с немецкоязычных ресурсов) на Delphi:

Посыл сообщения:

Код:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    edt1: TEdit;
    Button1: TButton;

    procedure Button1Click(Sender: TObject);

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
   aCopyData: TCopyDataStruct;
   hTargetWnd: HWND;
 begin
   with aCopyData do
   begin
     dwData := 0;
     cbData := StrLen(PChar(edt1.Text)) + 1;
     lpData := PChar(edt1.Text)
   end;
   // Search window by the window title
  // Fenster anhand des Titelzeilentext suchen
  hTargetWnd := FindWindowEx(0, 0, nil, PChar('MzXq@Receiver#10'));
   if hTargetWnd <> 0 then
     SendMessage(hTargetWnd, WM_COPYDATA, Longint(Handle), Longint(@aCopyData))
   else
     ShowMessage('No Recipient found!');
 end;
end.

Получение сообщения(при получении текст надписи должна отразить число, полученное из сообщения):

Код:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    lbl1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure WMCopyData(var Msg: TWMCopyData);
    message WM_COPYDATA;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin

end;
 // Recipient - Receive data 
// Empfanger - Daten empfangen

 procedure TForm1.WMCopyData(var Msg: TWMCopyData);
 var
   sText: array[0..99] of Char;
 begin
   // generate text from parameter 
  // anzuzeigenden Text aus den Parametern generieren 
  StrLCopy(sText, Msg.CopyDataStruct.lpData, Msg.CopyDataStruct.cbData);
   // write received text 
  // Empfangenen Text ausgeben
  lbl1.Caption := sText;
 end;
end.
Друг с другом коды "общаются".

Насколько я понимаю, чтобы повторить работу Delphi.au3, эти коды нужно объединить в один "приемо-передатчик". Мешает этому пока, видимо, некоторое мое непонимание работы Delphi.au3, т.к. там получение и отправка сообщений осуществляются через dll (в отличие от Дельфи), а с этой возможностью AutoIt я вообще еще не работал.

Чего удалось пока добиться - приложение на Дельфи реагирует на сообщение, посланное из AutoIt.au3, но числа пока переслать не удается. Пытаюсь разобраться :blink: :wacko:
 

beve

Осваивающий
Сообщения
104
Репутация
31
Вот простой пример "общения" программы на Autoit и программы на Delphi. В архиве уже собранные программы +исходники (для создания приложения на delphi была использована 7 портабельная версия Delphi).
Ссылка на скачивание архива
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
AutoIt программа - 1 файл.
Delph программа - 7 файлов.

:smile:
 
Автор
W

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
beve сказал(а):
Вот простой пример "общения" программы на Autoit и программы на Delphi. В архиве уже собранные программы +исходники (для создания приложения на delphi была использована 7 портабельная версия Delphi).
Ссылка на скачивание архива
Благодарю! Примеры очень интересные.
 
Верх