Что нового

Объектно-ориентированное программирование в AutoIt

Viktor1703

AutoIT Гуру
Сообщения
1,535
Репутация
413
Версия AutoIt должна быть 3.3.8.1 и выше :smile:

Код:
Global $tagInterface = 'MessageBox int(uint;wstr;wstr);' & _
					   'ToolTip void(wstr;int;int);'

Global $a__Methods[2] = [DllCallbackRegister('Test__MsgBox','int','ptr;uint;wstr;wstr'), _
						 DllCallbackRegister('Test__ToolTip','none','ptr;wstr;int;int')]


Func Test()
	Local $a__Result[2], $i__Number = UBound($a__Methods)
    If ($i__Number > 0) Then
	    $a__Result[1] = DllStructCreate('ptr VTable;ulong_ptr Number;ptr Method[' & $i__Number & ']')
	    DllStructSetData($a__Result[1], 'VTable', DllStructGetPtr($a__Result[1], 'Method'))
	    DllStructSetData($a__Result[1], 'Number', $i__Number)
		For $i = 1 To $i__Number
            DllStructSetData($a__Result[1], 'Method', DllCallbackGetPtr($a__Methods[$i - 1]), $i)
        Next
	    $a__Result[0] = ObjCreateInterface(DllStructGetPtr($a__Result[1]), '', $tagInterface, False)
	    If IsObj($a__Result[0]) Then
		    Return $a__Result
	    EndIf
	EndIf
	Return 0
EndFunc

Func Test__MsgBox($p_This, $i_Flag, $s_Title, $s_Text)
    Return MsgBox($i_Flag, $s_Title, $s_Text)
EndFunc

Func Test__ToolTip($p_This, $s_Text, $i_Left, $i_Top)
    ToolTip($s_Text, $i_Left, $i_Top)
EndFunc

Func Destroy($o__Object)
	If (IsArray($o__Object) And (Ubound($o__Object) >= 2)) Then
		$o__Object[0] = 0
		$o__Object[1] = 0
	    For $i = 0 To UBound($a__Methods) - 1
            DllCallbackFree($a__Methods[$i])
        Next
	EndIf
EndFunc

$oTest = Test()
$oTest[0].MessageBox(32, 'Title', 'Text')
$oTest[0].ToolTip('AutoIt v3 - это язык для написания сценариев, напоминающий BASIC и предназначенный для автоматизации Windows GUI', 20, 20)
Sleep(5000)
$oTest[0].ToolTip('', 0, 0)
Destroy($oTest)
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
интересный пример, :scratch: осталось придумать где он может пригодится
 
Автор
V

Viktor1703

AutoIT Гуру
Сообщения
1,535
Репутация
413
Ещё один пример...

Пример:
Код:
#include <GUIOOP.au3>

Global $oGui = GUIConstructor()

If IsObj($oGui) Then
    $hForm = $oGui.GUICreate('OOP AutoIt Gui', 500, 400, -1, -1, $oGui.GUI_SS_DEFAULT_GUI, 0, 0)
    $oGui.GUIBind($oGui.GUI_EVENT_CLOSE, 'OnExit')
	$oGui.GUISetState(@SW_SHOW, $hForm)
	While 1
		$oGui.GUIMainLoop($hForm)
	Wend
EndIf

Func OnExit($hWnd)
	If (MsgBox(4, $hWnd, 'Вы действительно желаете закрыть окно?') == 6) Then
	    Exit
	EndIf
EndFunc


GUIOOP.au3
Код:
OnAutoItExitRegister('GUIDestructor')

Global $aConstructor, $aGUILoop[1][2]

Global Const $IID__GUI = 'GUICreate hwnd(wstr;int;int;int;int;int;int;hwnd);' & _
						 'GUISetState int(int;hwnd);' & _
						 'GUIMainLoop void(hwnd);' & _
						 'GUIBind void(int;wstr);' & _
						 'GUI_EVENT_CLOSE int();' & _
						 'GUI_SS_DEFAULT_GUI int()'

Global Const $sMethods = 'GUI__GUICreate hwnd(ptr;wstr;int;int;int;int;int;int;hwnd);' & _
                         'GUI__GUISetState int(ptr;int;hwnd);' & _
						 'GUI__GUIMainLoop none(ptr;hwnd);' & _
						 'GUI__GUIBind none(ptr;int;wstr);' & _
						 'GUI__GUI_EVENT_CLOSE int(ptr);' & _
						 'GUI__GUI_SS_DEFAULT_GUI int(ptr)'

Func GUIConstructor()
	$aConstructor = ObjConstructor($IID__GUI, $sMethods)
	If IsArray($aConstructor) Then
	    $oObj = GetObject($aConstructor)
		If IsObj($oObj) Then
			Return $oObj
		EndIf
	EndIf
	Return 0
EndFunc

Func GUIDestructor()
	ObjDestructor($aConstructor)
EndFunc

Func ObjConstructor($sIID_Interface, $sMethods)
    Local $aObject[4], $tRegister, $aMethods, $iMethods, $tObject, $aFormat, $oObject
    $aMethods = StringRegExp($sMethods, '(.+?\(.+?\));?', 3)
    If IsArray($aMethods) Then
		$iMethods = UBound($aMethods)
		If $iMethods Then
			$tObject = DllStructCreate('ptr VTable;ulong_ptr Number;ptr Methods[' & $iMethods & ']')
			DllStructSetData($tObject, 'VTable', DllStructGetPtr($tObject, 'Methods'))
			DllStructSetData($tObject, 'Number', $iMethods)
			$tRegister = DllStructCreate('uint[' & $iMethods & ']')
			For $i = 0 To $iMethods - 1
				$aFormat = StringRegExp($aMethods[$i], '(.+?)\s+(.+?)\((.+?)\)', 3)
				If (IsArray($aFormat) And UBound($aFormat) == 3) Then
					$tCallback = DllCallbackRegister($aFormat[0], $aFormat[1], $aFormat[2])
					DllStructSetData($tRegister, 1, $tCallback, $i + 1)
                    DllStructSetData($tObject, 'Methods', DllCallbackGetPtr($tCallback), $i + 1)
				EndIf
            Next
			$oObject = ObjCreateInterface(DllStructGetPtr($tObject), '', $sIID_Interface, False)
			If IsObj($oObject) Then
				$aObject[0] = $iMethods
				$aObject[1] = DllStructGetPtr($tRegister)
				$aObject[2] = $tObject
				$aObject[3] = $oObject
				Return $aObject
			EndIf
		EndIf
	EndIf
	Return 0
EndFunc

Func GetObject(ByRef $aObject)
    If IsArray($aObject) And (UBound($aObject) == 4) And IsObj($aObject[3]) Then
		Return $aObject[3]
	EndIf
	Return 0
EndFunc

Func ObjDestructor(ByRef $aObject)
	Local $tRegister, $tMethod
	If (IsArray($aObject) And (UBound($aObject) == 4)) Then
		$tRegister = DllStructCreate('uint[' & $aObject[0] & ']', $aObject[1])
		For $i = 0 To $aObject[0] - 1
			$tMethod = DllStructGetData($tRegister, 1, $i + 1)
			If $tMethod Then
		        DllCallbackFree($tMethod)
			EndIf
	    Next
		$aObject[0] = 0
		$aObject[1] = 0
		$aObject[2] = 0
		$aObject[3] = 0
	EndIf
EndFunc

Func GUI__GUICreate($p_This, $s_Title, $i_Width, $i_Height, $i_Left, $i_Top, $i_Style, $i_ExStyle, $h_Parent)
    Return GUICreate($s_Title, $i_Width, $i_Height, $i_Left, $i_Top, $i_Style, $i_ExStyle, $h_Parent)
EndFunc

Func GUI__GUISetState($p_This, $i_Flag, $h_Wnd)
    Return GUISetState($i_Flag, $h_Wnd)
EndFunc

Func GUI__GUIBind($p_This, $i_Type, $s_Method)
	$aGUILoop[0][0] += 1
	ReDim $aGUILoop[$aGUILoop[0][0] + 1][UBound($aGUILoop, 2)]
	$aGUILoop[$aGUILoop[0][0]][0] = $i_Type
	$aGUILoop[$aGUILoop[0][0]][1] = $s_Method
EndFunc

Func GUI__GUIMainLoop($p_This, $hWnd)
	For $i = 1 To $aGUILoop[0][0]
		If (GUIGetMsg(0) == $aGUILoop[$i][0]) Then
			Call($aGUILoop[$i][1], $hWnd)
		EndIf
	Next
EndFunc

Func GUI__GUI_EVENT_CLOSE($p_This)
    Return -3
EndFunc

Func GUI__GUI_SS_DEFAULT_GUI($p_This)
	Return -2134245376
EndFunc
 

sims

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

sims

Осваивающий
Сообщения
184
Репутация
24
Viktor1703 [?]
Ну это же AutoIt, какие тут Namespace
Почему же так пессимистично?
Нужно попросить разработчиков чтобы они добавили поддержку пространства имен.
Это значительно упростит разработку и поддержку UDF, ведь не нужно будет думать о том, что могут совпасть имена, переменных, констант, функций и т. д. Даже если имена совпадут, но расположены в разных Namespace, это не приведет конфликту имен.
 
Автор
V

Viktor1703

AutoIT Гуру
Сообщения
1,535
Репутация
413
sims [?]
Почему же так пессимистично?Нужно попросить разработчиков чтобы они добавили поддержку пространства имен.Это значительно упростит разработку и поддержку UDF, ведь не нужно будет думать о том, что могут совпасть имена, переменных, констант, функций и т. д. Даже если имена совпадут, но расположены в разных Namespace, это не приведет конфликту имен.

Я думаю они откажутся это делать.

P.S. Кстати, кто знает, какую API функцию использует ObjCreateInterface ?



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

И ещё один пример

AutoIt

Код:
Global Const $IID_PUREBASIC = 'GetComputerName wstr();' & _
                              'GetUserName wstr();' & _
							  'CountSystemCPU int();'


$hObjectDll = DllOpen('object.dll')

$oObj = DllCreateObj($hObjectDll, $IID_PUREBASIC)

If IsObj($oObj) Then

	MsgBox(0, 'Имя компьютера',   $oObj.GetComputerName)
	MsgBox(0, 'Имя пользователя', $oObj.GetUserName)
	MsgBox(0, 'Количество ядер',  $oObj.CountSystemCPU)

EndIf

DllClose($hObjectDll)

Func DllCreateObj($hObjectDll, $IID_Interface)
	Local $oObj, $pRet = DllCall($hObjectDll, 'ptr', 'PureBasic')
    If ((Not @error) And IsArray($pRet) And $pRet[0]) Then
        $oObj = ObjCreateInterface($pRet[0], '', $IID_Interface, False)
		If IsObj($oObj) Then
			Return $oObj
		EndIf
    EndIf
	Return 0
EndFunc

PureBasic Dll

Код:
Interface PureBasic
  GetComputerName.s()
  GetUserName.s()
  CountSystemCPU.i()
EndInterface 

Structure PureBasic_OBJ
  *VTable
EndStructure 

Procedure.s PureBasic__ComputerName(*this.PureBasic)
  ProcedureReturn ComputerName()
EndProcedure 

Procedure.s PureBasic__UserName(*this.PureBasic)
  ProcedureReturn UserName()
EndProcedure 

Procedure.i PureBasic__CountSystemCPU(*this.PureBasic)
  ProcedureReturn CountCPUs(#PB_System_CPUs)
EndProcedure  

ProcedureDLL.l PureBasic()
  Protected *object.PureBasic_OBJ = AllocateMemory(SizeOf(PureBasic_OBJ)) 
  If *object
    *object\VTable = ?vTable
    ProcedureReturn *object
  EndIf
  ProcedureReturn 0
EndProcedure 

DataSection 
  vTable: 
  Data.i @PureBasic__ComputerName() 
  Data.i @PureBasic__UserName()
  Data.i @PureBasic__CountSystemCPU()
EndDataSection
 

sims

Осваивающий
Сообщения
184
Репутация
24
Viktor1703 [?]
Я думаю они откажутся это делать.
Да, это прибавит им работы, но оно того стоит, ведь упростит разработку. Стоит попросить. Возможно что сделают.


какую API функцию использует ObjCreateInterface
Не думаю что там используется какая-то специфичная WinAPI фукнция. Скорее всего просто создается интерфейс (таблица с указателями на функции и описанием их аргументов).


И ещё один пример
Не помешало бы добавить деструктор объекта чтобы его можно было бы уничтожить.
AutoIt
Код:
Global Const $IID_PUREBASIC = 'GetComputerName wstr();' & _
                              'GetUserName wstr();' & _
                              'CountSystemCPU int();' & _
							  'Destroy int();'


$hObjectDll = DllOpen('object.dll')

$oObj = DllCreateObj($hObjectDll, $IID_PUREBASIC)

If IsObj($oObj) Then

    MsgBox(0, 'Имя компьютера',   $oObj.GetComputerName)
    MsgBox(0, 'Имя пользователя', $oObj.GetUserName)
    MsgBox(0, 'Количество ядер',  $oObj.CountSystemCPU)

    $oObj.Destroy ; Уничтожение объекта.
	
EndIf


DllClose($hObjectDll)

Func DllCreateObj($hObjectDll, $IID_Interface)
    Local $oObj, $pRet = DllCall($hObjectDll, 'ptr', 'PureBasic')
    If ((Not @error) And IsArray($pRet) And $pRet[0]) Then
        $oObj = ObjCreateInterface($pRet[0], '', $IID_Interface, False)
        If IsObj($oObj) Then
            Return $oObj
        EndIf
    EndIf
    Return 0
EndFunc

dll
Код:
DeclareModule PB

Interface PureBasic
  GetComputerName.s()
  GetUserName.s()
  CountSystemCPU.i()
  Destroy()
EndInterface 

Declare.l PureBasic()

EndDeclareModule

Module PB

Structure PureBasic_OBJ
  *VTable
EndStructure 

Procedure.s GetComputerName(*this.PureBasic_OBJ)
  ProcedureReturn ComputerName()
EndProcedure 

Procedure.s GetUserName(*this.PureBasic_OBJ)
  ProcedureReturn UserName()
EndProcedure 

Procedure.i CountSystemCPU(*this.PureBasic_OBJ)
  ProcedureReturn CountCPUs(#PB_System_CPUs)
EndProcedure  

Procedure Destroy(*this.PureBasic_OBJ)
  ClearStructure(*this, PureBasic_OBJ)
  FreeMemory(*this)
EndProcedure

Procedure.l PureBasic()
  Protected *object.PureBasic_OBJ = AllocateMemory(SizeOf(PureBasic_OBJ)) 
  If *object
    *object\VTable = ?vTable
    ProcedureReturn *object
  EndIf
  ProcedureReturn 0
EndProcedure 

DataSection 
  vTable: 
  Data.i @GetComputerName() 
  Data.i @GetUserName()
  Data.i @CountSystemCPU()
  Data.i @Destroy()
EndDataSection

EndModule


ProcedureDLL PureBasic()
  ProcedureReturn PB::PureBasic()
EndProcedure
 
Автор
V

Viktor1703

AutoIT Гуру
Сообщения
1,535
Репутация
413
sims [?]
Не помешало бы добавить деструктор объекта чтобы его можно было бы уничтожить.

Ну это пример и врятли кто-то в ближайшее время будет пользоваться этим

Да, это прибавит им работы, но оно того стоит, ведь упростит разработку. Стоит попросить. Возможно что сделают.

Было бы не плохо, тогда можно будет портировать Qt под AutoIt (естественно если будет полноценный ООП)

Не думаю что там используется какая-то специфичная WinAPI фукнция. Скорее всего просто создается интерфейс (таблица с указателями на функции и описанием их аргументов).

хм, возможно, однако объект созданный через ObjCreate и ObjCreateInterface не возвращает ни чего, точнее в консоли пустое значение, хотел добавить сам ObjCreateInterface в dll чтобы при вызове она возвращала уже готовый объект а не указатель на таблицу.
 

sims

Осваивающий
Сообщения
184
Репутация
24
Viktor1703 [?]
тогда можно будет портировать Qt под AutoIt
В каком смысле?


объект созданный через ObjCreate и ObjCreateInterface не возвращает ни чего
Он может возвращать, скажем, номер объекта, а не сам объект или указатель на него.

хотел добавить сам ObjCreateInterface в dll
Для этого нужно писать dll на AutoIt или сделать аналог ObjCreateInterface, если знаете принцип создания объекта. Но думаю что это не так просто осуствить, ведь наверняка объект регистрируется где-то в интерпретаторе и даже если его вернуть из dll, то это не сработает.
 
Автор
V

Viktor1703

AutoIT Гуру
Сообщения
1,535
Репутация
413
Garrett

Это конечно всё хорошо, но мало чем помогает, дело в том что там примеры на C++ а он сам по себе ООП, в примерах заметил

Код:
pCF = new MathClassFactory;

где pCF содержит уже готовый объект, то есть мне нужны сами низа, как получить экземпляр объекта имея указатель на таблицу функций с помощью WinAPI (подозреваю Co и Ole функции), если возвращать указатель на таблицу, то приходится использовать ObjCreateInterface, если возвращать указатель на интерфейс, то естественно не работает.... для получения объекта с помощью DllCall в тип возвращаемого значения вроде нужно писать idispatch
 

sims

Осваивающий
Сообщения
184
Репутация
24
Viktor1703 [?]
как получить экземпляр объекта
Попробуйте передавать экземпляр объекта.
Код:
ProcedureDLL PureBasic()
  Static Object.PB::PureBasic
  
  Object = PB::PureBasic()
  
  ProcedureReturn @Object
EndProcedure
Только не думаю что это сработает, потому что AutoIt не знает ничего об классе (его методах, их аргументах и т. д.).
 

sims

Осваивающий
Сообщения
184
Репутация
24
Оно и понятно, ведь AutoIt ничего не знает от содержимом объекта. В объекте не хранятся его имена. Как AutoIt узнает к какому методу обратиться?
 
Автор
V

Viktor1703

AutoIT Гуру
Сообщения
1,535
Репутация
413
sims [?]
Как AutoIt узнает к какому методу обратиться?

Для этого и придуман Interface, видимо ObjCreateInterface создаёт новый интерфейс который принимается в 3-й параметр и вызывает методы, хз, я не силён в этих тонкостях и не углублялся. :scratch:
 

sims

Осваивающий
Сообщения
184
Репутация
24
Может вам такой вариант подойдет?
Код:
$hObjectDll = DllOpen('object.dll')

$oObj = DllCreateObj($hObjectDll)

If IsObj($oObj) Then

    MsgBox(0, 'Имя компьютера',   $oObj.GetComputerName)
    MsgBox(0, 'Имя пользователя', $oObj.GetUserName)
    MsgBox(0, 'Количество ядер',  $oObj.CountSystemCPU)

    $oObj.Destroy ; Уничтожение объекта.
	
EndIf


DllClose($hObjectDll)

Func DllCreateObj($hObjectDll)
   Local $oObj, $IID_PUREBASIC = DllCall($hObjectDll, 'wstr', 'IID')
	   
   If ((Not @error) And IsArray($IID_PUREBASIC)) Then
	   
       local $pRet = DllCall($hObjectDll, 'ptr', 'New')
       If ((Not @error) And IsArray($pRet) And $pRet[0]) Then
		  
        $oObj = ObjCreateInterface($pRet[0], '', $IID_PUREBASIC[0], False)
        If IsObj($oObj) Then
            Return $oObj
		 EndIf
		 
      endif
    EndIf
    Return 0
EndFunc
Код:
DeclareModule PB

#IID = "GetComputerName wstr();"+
       "GetUserName wstr();"+
       "CountSystemCPU int();"+
       "Destroy int();"

Declare.l PureBasic()

EndDeclareModule

Module PB

Structure PureBasic_OBJ
  *VTable
EndStructure 

Procedure.s GetComputerName(*this.PureBasic_OBJ)
  ProcedureReturn ComputerName()
EndProcedure 

Procedure.s GetUserName(*this.PureBasic_OBJ)
  ProcedureReturn UserName()
EndProcedure 

Procedure.i CountSystemCPU(*this.PureBasic_OBJ)
  ProcedureReturn CountCPUs(#PB_System_CPUs)
EndProcedure  

Procedure Destroy(*this.PureBasic_OBJ)
  ClearStructure(*this, PureBasic_OBJ)
  FreeMemory(*this)
EndProcedure

Procedure.l PureBasic()
  Protected *object.PureBasic_OBJ = AllocateMemory(SizeOf(PureBasic_OBJ)) 
  If *object
    *object\VTable = ?vTable
    ProcedureReturn *object
  EndIf
  ProcedureReturn 0
EndProcedure 

DataSection 
  vTable: 
  Data.i @GetComputerName() 
  Data.i @GetUserName()
  Data.i @CountSystemCPU()
  Data.i @Destroy()
EndDataSection

EndModule


ProcedureDLL New()
  ProcedureReturn PB::PureBasic()
EndProcedure

ProcedureDLL.s IID()
  ProcedureReturn PB::#IID
EndProcedure
Отличие в том, что в скрипте не хранится описание методов объекта, и функция DllCreateObj скрипта, может создать объект из любой dll, без необходимости хранить в скрипте описание объекта.
 
Верх