Что нового

НЕофициальный FAQ AutoIt

Статус
Закрыто для дальнейших ответов.
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Вопрос:
Почему лучше использовать BitOR для добавления значении констант (например в стилях GUI)?

Ответ:
При обычном сложении значении констант, мы получаем кашу из чисел. А при использовании BitOR, существующее значение в добавляемом числе, повторяться не будет (т.е не будет добавляться содержащееся число). Вот пример сравнения:

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

$bUseBitOR = True ;Поставьте на False и увидите ошибку в стилизации GUI

$iStyles = $GUI_SS_DEFAULT_GUI + $WS_SIZEBOX

;Вторично пытаемся добавить стиль $WS_SIZEBOX (иногда это происходит если стили берутся к примеру из файла конфигурации)
If $bUseBitOR Then
	$iStyles = BitOR($iStyles, $WS_SIZEBOX)
Else
	$iStyles += $WS_SIZEBOX
EndIf

$hGUI = GUICreate("Test Script", 300, 200, -1, -1, $iStyles)
GUISetState(@SW_SHOW, $hGUI)

While 1
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE
			Exit
	EndSwitch
WEnd
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Вопрос:
Как правильно “запоминать” позицию GUI для последующих запусков?

Ответ:
Многие пытаются реализовать возможность сохранения позиции и размера GUI для последующих запусков программы.
Однако очень часто делают невольные ошибки, и даже не подозревая этого.

Например, знаете ли вы, что... если закрыть свёрнутое окно из панели задач, то размер окна будет сохранён неверно, будет что-то типа «-32000 x -32000».

Вот пример показывающий как предотвратить эту проблему, и другие, связанные с сохранением и восстановлением позиции GUI, а также как это делается оптимальнее всего (на мой взгляд):

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

;Отключаем обработку событии $GUI_EVENT_MINIMIZE, $GUI_EVENT_RESTORE и $GUI_EVENT_MAXIMIZE
;Это нам придётся делать самим, т.к нам нужно получать размеры окна ДО его сворачивания
Opt('GUIEventOptions', 1)
OnAutoItExitRegister('_OnExit')

Global $sConfig_File                = @ScriptDir & '\' & StringTrimRight(@ScriptName, 4) & '_Config.ini'

Global $aGUI_Last_Pos

Global $iDef_GUILeft                = -1
Global $iDef_GUITop                 = -1
Global $iDef_GUIWidth               = 700
Global $iDef_GUIHeight              = 550

Global $iGUI_Left                   = IniRead($sConfig_File, 'Window Settings', 'Left', $iDef_GUILeft)
Global $iGUI_Top                    = IniRead($sConfig_File, 'Window Settings', 'Top', $iDef_GUITop)
Global $iGUI_Width                  = IniRead($sConfig_File, 'Window Settings', 'Width', $iDef_GUIWidth)
Global $iGUI_Height                 = IniRead($sConfig_File, 'Window Settings', 'Height', $iDef_GUIHeight)

_GUIFixPos()

$hMain_GUI = GUICreate('Remember GUI Size Demo', $iGUI_Width, $iGUI_Height, $iGUI_Left, $iGUI_Top, BitOR($GUI_SS_DEFAULT_GUI, $WS_SIZEBOX))
GUISetState(@SW_SHOW, $hMain_GUI)

While 1
    $nMsg = GUIGetMsg()
    
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit
        Case $GUI_EVENT_MINIMIZE
            ;Получаем размеры окна ДО его сворачивания
            $aGUI_Last_Pos = _WinGetPos($hMain_GUI)
            
            ;Теперь можно и свернуть окно :), делаем вручную, т.к в начале мы отключили обработку этого события
            GUISetState(@SW_MINIMIZE, $hMain_GUI)
        Case $GUI_EVENT_RESTORE
            ;Восстанавливаем окно
            GUISetState(@SW_RESTORE, $hMain_GUI)
        Case $GUI_EVENT_MAXIMIZE
            ;Разворачиваем окно
            GUISetState(@SW_MAXIMIZE, $hMain_GUI)
    EndSwitch
WEnd

Func _GUIFixPos()
	If $iGUI_Left = -1 Or $iGUI_Left = '' Or $iGUI_Left < -$iGUI_Width Or $iGUI_Left > (@DesktopWidth - 50) Then
		$iGUI_Left = $iDef_GUILeft
	EndIf
	
	If $iGUI_Top = -1 Or $iGUI_Top = '' Or $iGUI_Top < -$iGUI_Height Or $iGUI_Top > (@DesktopHeight - 50) Then
		$iGUI_Top = $iDef_GUITop
	EndIf
	
	If $iGUI_Width > (@DesktopWidth - 50) Then
		$iGUI_Width = $iDef_GUIWidth
	EndIf
	
	If $iGUI_Height > (@DesktopHeight - 50) Then
		$iGUI_Height = $iDef_GUIHeight
	EndIf
EndFunc

Func _WinGetPos($hWin)
	Local $aWin_Pos = WinGetPos($hWin)
	If @error Then Return SetError(1)
	
	Local $aClient_Size = WinGetClientSize($hWin)
	If @error Then Return SetError(2)
	
	$aWin_Pos[2] = $aClient_Size[0]
	$aWin_Pos[3] = $aClient_Size[1]
	
	Return $aWin_Pos
EndFunc

Func _OnExit()
    If Not IsArray($aGUI_Last_Pos) Then
        $aGUI_Last_Pos = _WinGetPos($hMain_GUI)
    EndIf
    
    If UBound($aGUI_Last_Pos) = 4 Then
        IniWrite($sConfig_File, 'Window Settings', 'Left', $aGUI_Last_Pos[0])
        IniWrite($sConfig_File, 'Window Settings', 'Top', $aGUI_Last_Pos[1])
        IniWrite($sConfig_File, 'Window Settings', 'Width', $aGUI_Last_Pos[2])
        IniWrite($sConfig_File, 'Window Settings', 'Height', $aGUI_Last_Pos[3])
    EndIf
EndFunc
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Вопрос:
Как выполнить перенаправление по ссылке, и получить перенаправленную ссылку?

Ответ:
Метод используя объект WinHttp:

Нужно разрешить перенаправление и проверять Location:
Код:
$oHTTP = ObjCreate("WinHttp.WinHttpRequest.5.1")
$oHTTP.Option(6) = False ;WinHttpRequestOption_EnableRedirects
$oHTTP.Open("GET", "http://creator-lab.ucoz.ru/load/0-0-1-52-20")
$oHTTP.Send()
$oHTTP.WaitForResponse
$sNewLocation = $oHTTP.GetResponseHeader("Location")
ConsoleWrite($sNewLocation & @LF)


Метод используя функции TCP*:

Код:
$sHead = _HTTPGetResponse("creator-lab.ucoz.ru", "/load/0-0-1-52-20", "HEAD")

If StringRegExp($sHead, "(?i)Content-Type:(.*?)html") Then
	$sHead = _HTTPGetResponse("creator-lab.ucoz.ru", "/load/0-0-1-52-20", "GET")
EndIf

$sNewLocation = StringRegExpReplace($sHead, '(?s).*\r?\nLocation:\h*([^\r\n]+)\r?\n.*', '\1')
MsgBox(64, 'Title', 'Location: ' & @CRLF & $sNewLocation)

Func _HTTPGetResponse($sHost, $sPage, $sRequest = "HEAD")
    TCPStartup()
    
    Local $sName_To_IP = TCPNameToIP($sHost)
    Local $iSocket = TCPConnect($sName_To_IP, 80)
    
    If $iSocket = -1 Then
        TCPShutdown()
        Return SetError(1, 0, "")
    EndIf
    
    Local $sCommand = $sRequest & " " & $sPage & " HTTP/1.1" & @CRLF
    $sCommand &= "Host: " & $sHost & @CRLF
    $sCommand &= "User-Agent: AutoIt/" & @AutoItVersion & " (Windows; U; Windows NT 5.1; en-US; rv:1.8.1)" & @CRLF
    $sCommand &= "Referer: " & $sHost & @CRLF
    $sCommand &= "Connection: close" & @CRLF & @CRLF
    
    Local $BytesSent = TCPSend($iSocket, $sCommand)
    
    If $BytesSent = 0 Then
        TCPShutdown()
        Return SetError(2, @error, 0)
    EndIf
    
    Local $sRecv = "", $sCurrentRecv
    
    While 1
        $sCurrentRecv = TCPRecv($iSocket, 16)
        If @error <> 0 Then ExitLoop
        If $sCurrentRecv <> "" Then $sRecv &= $sCurrentRecv
    WEnd
    
    TCPCloseSocket($iSocket)
    TCPShutdown()
    
    Return $sRecv
EndFunc
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Вопрос:
Почему лучше использовать GUICtrl* вместо Control*?

Ответ:
Все Control* функции сначала ищут родительское окно, затем перечисляют все его элементы для определения необходимого ID, и только затем посылают соответствующее сообщение. В случае работы с текстом, например ControlGetText, все ещё намного сложнее, т.к. необходимо перенаправить поток ввода из чужого процесса в свой.
GUICtrl* функции работают напрямую и только в своём процессе, что намного проще и быстрее.

Control* функции нужно использовать только для окон, созданных не AutoIt средствами.
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Вопрос:
Как позволить запуск только одной копии программы?

Ответ:
Поместите следующий блок кода в начало вашего скрипта:
Код:
If WinExists('[CLASS:AutoIt v3;TITLE:' & @ScriptName & ']') Then
    MsgBox(48, @ScriptName, 'Позволено запускать только одну копию программы!' & @CRLF & @CRLF & 'ОК ==> ВЫХОД')
    
    ;Раскоментируйте следующую строчку если нужно активировать окно вашей программы при повторном её запуске
    ;WinActivate('Имя вашей программы')
    
    Exit
EndIf

AutoItWinSetTitle(@ScriptName)
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Вопрос:
Как предотвратить критические ошибки скрипта?

Ответ:
Таких ошибок есть несколько, рассмотрим наиболее “популярную”...
Ошибка при использовании массива, наверное самая распространённая ошибка среди новичков:

В большинстве случаев скрипт работает без сбоев, но происходит некое условие (которое скриптер не предусмотрел), и скрипт “вылетает” с ошибкой, например:
Subscript used with non-Array variable
такое происходит если выполняется попытка обратиться к переменной, которая не является массивом, как к таковому.
В данном случае, перед использованием переменной будет достаточно проверять её функцией IsArray, и если возвращает 1, значит можно обращаться к переменной как к массиву (хотя есть и некоторые нюансы, см. далее).

Следующая ошибка проявляется если обратиться к несуществующему индексу или ячейке массива:
Array variable has incorrect number of subscripts or subscript dimension range exceeded
это также касается обращения к массиву как к двумерному (к примеру), в то время когда он является одномерным, ну и наоборот.
С размерностью обычно проблем нет, скриптер должен точно знать какая размерность у используемого массива.
А вот с обращением к несуществующему индексу, часто бывают проблемы.
Решаются они также довольно просто, перед обращением к массиву, нужно всего лишь проверять его размер, делается при помощи функции Ubound.

IsArray и Ubound можно проверять в связке (последовательно), хотя на самом деле достаточно проверять только через Ubound, т.к он в любом случае вернёт 0 если переменная не является массивом.

Примеры правильного использования и обращения к массивам:

Первая проверка на массивность, и вторая на размер массива:
Код:
Dim $vArray = StringSplit('1,2,3,4,5', ',')

;Если переменная $vArray является массивом, то...
If IsArray($vArray) Then
	;Выводим первый (нулевой) элемент массива, т.к мы уже точно знаем что эта переменная является массивом, 
	;а это значит что она имеет как минимум один элемент
	MsgBox(64, '$vArray[0]', $vArray[0])
	
	;Проверяем что массив имеет более чем 4 элемента (как известно, нумерация начинается с нуля)
	If UBound($vArray) > 4 Then
		MsgBox(64, '$vArray[4]', $vArray[4])
	EndIf
EndIf


Проверка двумерного массива:
Код:
$iCols = 2 ;Число колонок
Dim $vArray[6][$iCols] = [[5], [1, 'Data1'], [2, 'Data2'], [3, 'Data3'], [4, 'Data4'], [5, 'Data5']]

;Если массив двумерный (его размерность равна 2-ум), то...
If UBound($vArray, 0) = 2 Then
	;Выводим первый (нулевой) элемент двумерного массива, т.к мы уже точно знаем что эта переменная является массивом, 
	;а это значит что она имеет как минимум один элемент в каждой колонке
	MsgBox(64, '$vArray[0][0]', $vArray[0][0])
EndIf

;Проверяем размер первой и второй колонки, если первая больше чем 3 а вторая больше чем 1, то...
If UBound($vArray, 1) > 3 And UBound($vArray, 2) > 1 Then
	;Выводим на экран значение 4-ой ячейки во второй колонке
	;(не забываем что нумерация начинается с 0-ля, см. справку)
	MsgBox(64, '$vArray[3][1]', $vArray[3][1])
EndIf




Неправильный пример:
Код:
Dim $vArray = StringSplit('1,2,3,4,5', ',')

If IsArray($vArray) Then
	MsgBox(64, '$vArray[0][0]', $vArray[0][0])
EndIf


в данном случае мы обращаемся к одномерному массиву как к двумерному, не проверив заранее его размерность, а только его тип (массив или нет).

Исправленный пример:
Код:
Dim $vArray = StringSplit('1,2,3,4,5', ',')

If IsArray($vArray) And Ubound($vArray, 0) = 2 Then
	MsgBox(64, '$vArray[0][0]', $vArray[0][0])
EndIf


в данном случае естественно вывода на экран не будет, т.к переменная $vArray является одномерным массивом.



На втором месте среди распространённых ошибок, наверное являются ошибки, связанные с объектами.
Как и в случае с массивами, перед использованием свойств объекта, следует проверять, является ли используемый объект таковым, или нет.

Неправильный пример:
Код:
$oDict = ObjCreate('Scripting.Dictionary')

$oDict.Add('Key', 'Item')
MsgBox(0, @ScriptName, $oDict.Item('Key'))


Исправленный пример:
Код:
$oDict = ObjCreate('Scripting.Dictionary')

If IsObj($oDict) Then
	$oDict.Add('Key', 'Item')
	MsgBox(0, @ScriptName, $oDict.Item('Key'))
EndIf


Также смотрите справку к функции ObjEvent, с помощью которой можно контролировать события и ошибки вызванные использованием объектов.
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Последнее редактирование:
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Вопрос:
Как отловить критические ошибки скрипта?

Ответ:
Два основные варианта:
1) OnAutoItErrorRegister / AutoItErrorHandler - Обработчик критических ошибок - Обработка критических ошибок AutoIt
2) _AutoItErrorTrap.au3 (UDF) - Error detection in AutoIt scripts! (используется хук процесса)

Упрощённый пример второго варианта:
Код:
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPI.au3>

If Not @Compiled Then
	MsgBox(48, @ScriptName, 'Please compile.')
	Exit
EndIf

; Call Back variables
Global $hAET_CBTPROC_CALLBKERROR, $hAET_CBTPROC_HOOKERROR

_AutoItErrorTrap()

$Form1 = GUICreate("Form1", 262, 113, 192, 124)
$Button1 = GUICtrlCreateButton("Вызвать критическую ошибку", 8, 8, 243, 97)
GUISetState(@SW_SHOW)

While 1
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE
			Exit
		Case $Button1
			Dim $array[10]
			MsgBox(0, "Это ошибка", $array[15])
	EndSwitch
WEnd

Func _ErrHandler($data)
	MsgBox(16, "Ошибка", "Обнаружена неустранимая ошибка: " & $data)
	
	Local $sRelaunchLine = @AutoItExe & ' "' & @ScriptFullPath & '"'
	If @Compiled Then $sRelaunchLine = @ScriptFullPath
	Run($sRelaunchLine, @ScriptDir)
EndFunc

Func _AutoItErrorTrap()
	Local $iAET_THREADID
	
	$iAET_THREADID = _WinAPI_GetCurrentThreadId()
	$hAET_CBTPROC_CALLBKERROR = DllCallbackRegister("__CBTProc_ErrorTrap", "int", "int;int;int") ; регистрируем коллбэк, который будет перехватывать сообщения об ошибках
	
	If Not $hAET_CBTPROC_CALLBKERROR Then
		Return 0
	EndIf
	
	$hAET_CBTPROC_HOOKERROR = _WinAPI_SetWindowsHookEx($WH_CBT, DllCallbackGetPtr($hAET_CBTPROC_CALLBKERROR), 0, $iAET_THREADID) ; и вешаем его на перехват окон
	
	If Not $hAET_CBTPROC_HOOKERROR Then
		DllCallbackFree($hAET_CBTPROC_CALLBKERROR)
		Return 0
	EndIf
	
	Return 1
EndFunc

Func __CBTProc_ErrorTrap($nCode, $wParam, $lParam)
	If $nCode <> 5 Then
		Return _WinAPI_CallNextHookEx($hAET_CBTPROC_HOOKERROR, $nCode, $wParam, $lParam)
	EndIf
	
	If Not _WinAPI_FindWindow("#32770", "AutoIt Error") Then
		Return _WinAPI_CallNextHookEx($hAET_CBTPROC_HOOKERROR, $nCode, $wParam, $lParam)
	EndIf
	
	Local $hAET_ERROR_HWND = HWnd($wParam)
	$hAET_GETERROR = ControlGetText($hAET_ERROR_HWND, "", "Static2")
	
	If IsDeclared("__iLineNumber") Then
		$hAET_GETERROR = StringRegExpReplace($hAET_GETERROR, "\d+[0-9]", Eval("__iLineNumber") & @CRLF)
		$hAET_GETERROR = StringReplace($hAET_GETERROR, "Х", @CRLF & "Module: Main/", 1)
	EndIf
	
	$hAET_GETERROR = StringReplace($hAET_GETERROR, @LF, @CRLF)
	
	$Data = StringSplit($hAET_GETERROR, @CRLF)
	$Line = StringSplit($Data[1], " ")
	
	_WinAPI_UnhookWindowsHookEx($hAET_CBTPROC_HOOKERROR)
	_WinAPI_DestroyWindow($hAET_ERROR_HWND)
	
	;->>>>> ********** Здесь место для вставки пользовательских функций!
	
	_ErrHandler("Line " & $Line[2] & "; " & $Data[$Data[0]])
	
	;<<<<<- **********
	
	Return _WinAPI_CallNextHookEx($hAET_CBTPROC_HOOKERROR, $nCode, $wParam, $lParam)
EndFunc

(скрипт должен быть скомпилирован)

:
За идею вопроса спасибо veretragna
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Вопрос:
Как сопоставить номер строки из ошибки скомпилированного скрипта с исходником?

Ответ:
Применить следующий скрипт к исходнику скомпилированного скрипта:

Код:
#include <WinAPIFiles.au3>

Global $sAutoItExe = @AutoItExe ;Should be 3.3.12.0 / 3.3.9.4 / 3.3.8.X / 3.3.6.X
;Global $sAutoItExe = 'D:\AutoIt_Versions\AutoIt_3.3.12.0\AutoIt3.exe'

$sFile = FileOpenDialog('Select script file (source from your compiled script)...', @ScriptDir, 'AutoIt v3 Script (*.au3)')
If @error Then Exit

$iLine = InputBox('Get error code line', 'Please enter error line number from compiled script:' & @CRLF & @CRLF & '(the AutoIt version of selected source file should match the version that used to compile the script)', '')
If @error Then Exit

SplashTextOn('Generating error code line', 'Please wait...', 200, 50, -1, -1, BitOR(16, 32), '', 12, 800)
$sCodeLine = _AU3_GetErrLineCode($sFile, $iLine)
SplashOff()

MsgBox(64, 'Result', StringFormat('Error code line (#%i) for <%s>:\r\n%s', $iLine, StringRegExpReplace($sFile, '^.*\\', ''), $sCodeLine))

Func _AU3_GetErrLineCode($sScript_File, $iLine)
	Local $sSrc_Raw, $iPos1, $iPos2, $iLen
	
	$sSrc_Raw = _AU3_StripToRaw($sScript_File)
	
	If @error Then
		Return SetError(1, 0, '')
	EndIf
	
	$sSrc_Raw = StringStripWS($sSrc_Raw, 3)
	$iPos1 = StringInStr($sSrc_Raw, @LF, 2, $iLine - 1)
	$iPos2 = StringInStr($sSrc_Raw, @LF, 2, $iLine)
	$iLen = ($iPos2 > 0 ? $iPos2 - $iPos1 : -1)
	
	Return StringStripWS(StringMid($sSrc_Raw, $iPos1, $iLen), 3)
EndFunc

Func _AU3_StripToRaw($sSrcFile, $bReset = False)
	If $sSrcFile = '' Or Not FileExists($sSrcFile) Then
		Return SetError(1, 0, 0)
	EndIf
	
	Local Static $sSrcRaw = ''
	Local Static $sInclds = '|'
	
	If $bReset Then
		$sSrcRaw = ''
		$sInclds = '|'
	EndIf
	
	Local $sInclude = '', $sLine = '', $iPos = 0, $sFName = '', $sFnc = '', $aFnc, $aVars
	Local $sScriptDir = StringRegExpReplace($sSrcFile, '\\[^\\]+$', '')
	Local $sRead = FileRead($sSrcFile)
	
	;!!! Strip comment block here (before checking #include-once)...
	_AU3_StringStripCommentBlocks($sRead)
	
	If StringRegExp($sRead, '(?mi)^\h*#include-once') Then
		If StringInStr($sInclds, '|' & $sSrcFile & '|', 2) Then
			$sRead = StringRegExpReplace($sRead, '(?msi)\R?^\h*#include-once.*', '')
		Else
			$sInclds &= $sSrcFile & '|'
		EndIf
	EndIf
	
	$sRead = StringStripCR($sRead)
	Local $aRead = StringSplit($sRead, @LF)
	
	For $i = 1 To $aRead[0]
		If StringStripWS($aRead[$i], 8) = '' Then
			ContinueLoop
		EndIf
		
		If StringRegExp($aRead[$i], '(?i)^\h*(;|#include-once|#pragma\h+compile\()') Then
			ContinueLoop
		EndIf
		
		_AU3_StringStripComments($aRead[$i])
		
		If Not StringRegExp($aRead[$i], '(?i)^\h*#include\h*[<"'']') Then
			$sLine = $aRead[$i]
			
			;Merge broken (with _) lines
			If StringRegExp($aRead[$i], '_\h*$') Then
				$sLine = StringRegExpReplace($aRead[$i], '^\h+|[_\h]+$', '')
				
				While 1
					$i += 1
					
					If $i >= $aRead[0] Then
						ExitLoop
					EndIf
					
					_AU3_StringStripComments($aRead[$i])
					$aRead[$i] = StringRegExpReplace($aRead[$i], '[_\h]+$', '')
					
					If @extended = 0 Then
						$sLine &= ' ' & StringStripWS($aRead[$i], 1)
						ExitLoop
					EndIf
					
					$sLine &= ' ' & StringStripWS($aRead[$i], 1)
				WEnd
			EndIf
			
			$sSrcRaw &= StringStripWS($sLine, 1) & @CRLF
			
			ContinueLoop
		EndIf
		
		;Get include path
		$sInclude = _AU3_IncludeToPath($aRead[$i], $sScriptDir)
		
		If Not @error Then
			;Recursive call for include
			$sSrcRaw = _AU3_StripToRaw($sInclude)
		EndIf
	Next
	
	Return $sSrcRaw
EndFunc

Func _AU3_IncludeToPath($sInclude, $sScriptDir = @ScriptDir)
	Local $aRegExp, $aRet, $sSYS, $sAU3, $sWorkDir
	Local $iError = 0, $sRet = ''
	
	$aRegExp = StringRegExp($sInclude, '(?i)^\h*#include\h+(<|"|'')([^>"'']+)(?:>|"|'')\h*(;.*?)?$', 3)
    
    If UBound($aRegExp) < 2 Then
		Return SetError(1, 0, '')
	EndIf
	
    $sInclude = $aRegExp[1]
	
	;Get AutoIt Include folder
	Local Static $sAutoIt_Incl_Dir = StringRegExpReplace($sAutoItExe, '\\[^\\]+$', '') & '\Include'
	
	;Get User Include folders
	Local Static $aUDL = StringRegExp(RegRead('HKCU\Software\AutoIt v3\AutoIt', 'Include'), '([^;]+)(?:;|$)', 3)
	
	While 1
		;Check include type 4 (include with full path)
		If Not _WinAPI_PathIsRelative($sInclude) Then
			If FileExists($sInclude) Then
				$sRet = $sInclude
			Else
				$iError = 2
			EndIf
			
			ExitLoop
		EndIf
		
		;Set Current & AutoIt Include file
		$sAU3 = $sScriptDir & '\' & $sInclude
		$sSYS = $sAutoIt_Incl_Dir & '\' & $sInclude
		
		;Check include type 1 and 2 (before user includes check)
		If $aRegExp[0] == '<' Then
			If FileExists($sSYS) Then
				$sRet = $sSYS
				ExitLoop
			EndIf
		ElseIf $aRegExp[0] == '"' Or $aRegExp[0] == "'" Then
			If FileExists($sAU3) Then
				$sRet = $sAU3
				ExitLoop
			EndIf
		EndIf
		
		;Check include type 3 (search in user includes)
		For $i = 0 To UBound($aUDL) - 1
			$aUDL[$i] &= '\' & $sInclude
			
			If FileExists($aUDL[$i]) Then
				$sRet = $aUDL[$i]
				ExitLoop 2
			EndIf
		Next
		
		;Check include type 1 and 2 (after user includes check)
		If $aRegExp[0] == '<' Then
			If FileExists($sAU3) Then
				$sRet = $sAU3
				ExitLoop
			EndIf
		ElseIf $aRegExp[0] == '"' Or $aRegExp[0] == "'" Then
			If FileExists($sSYS) Then
				$sRet = $sSYS
				ExitLoop
			EndIf
		EndIf
		
		;Include file not found
		$iError = 3
		ExitLoop
	WEnd
	
	If Not $iError Then
		$sWorkDir = @WorkingDir
		
		If $sWorkDir <> $sScriptDir Then
			FileChangeDir($sScriptDir)
		EndIf
		
		$sRet = _WinAPI_GetFullPathName($sRet)
		
		If @error Or $sRet = '' Then
			$iError = 4
		EndIf
		
		If $sWorkDir <> $sScriptDir Then
			FileChangeDir($sWorkDir)
		EndIf
	Else
		$sRet = ''
	EndIf
	
    Return SetError($iError, 0, $sRet)
EndFunc

Func _AU3_StringStripCommentBlocks(ByRef $sString)
	Local $aSplit = StringSplit(StringStripCR($sString), @LF)
	Local $iCmntsStart_Count
	
	$sString = ''
	
	For $i = 1 To $aSplit[0]
		If StringRegExp($aSplit[$i], '(?i)^\h*#(cs|comments-start)([\h;].*)?$') Then
			$iCmntsStart_Count = 1
			
			While 1
				If $i + 1 >= $aSplit[0] Then
					ExitLoop
				EndIf
				
				$i += 1
				
				If StringRegExp($aSplit[$i], '(?i)^\h*#(cs|comments-start)([\h;].*)?') Then
					$iCmntsStart_Count += 1
					ContinueLoop
				EndIf
				
				If StringRegExp($aSplit[$i], '(?i)^\h*#(ce|comments-end)([\h;].*)?') Then
					$iCmntsStart_Count -= 1
					
					If $iCmntsStart_Count <= 0 Then
						ExitLoop
					EndIf
				EndIf
			WEnd
			
			ContinueLoop
		EndIf
		
		$sString &= $aSplit[$i] & @CRLF
	Next
EndFunc

Func _AU3_StringStripComments(ByRef $sString)
	Local $aStrs = StringRegExp($sString, '("[^"]*"|''[^'']*'')', 3)
	Local $sStrs = StringRegExpReplace($sString, '("[^"]*"|''[^'']*'')', '#~_~@~_~#')
	Local $iInStr = StringInStr($sStrs, ';', 2)
	
	If $iInStr = 0 Then
		Return
	EndIf
	
	$sString = StringStripWS(StringLeft($sStrs, $iInStr - 1), 2)
	
	For $i = 0 To UBound($aStrs) - 1
		$sString = StringReplace($sString, '#~_~@~_~#', $aStrs[$i], 1, 2)
	Next
EndFunc


также см. предыдущие вопросы.
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Вопрос:
Как добавить цифровую подпись в свои программы?

Ответ:
Подробнее тут
Ответ подразумевает использование Compiler Wrapper из сборки SciTE от Yashied.
(устарело)

Вариант №1:
Для начала нужно создать сертификат:
Скачиваем и устанавливаем Windows SDK отсюда.
Далее забираем с папки C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin следующие файлы:
Код:
makecert.exe
pvk2pfx.exe
signtool.exe
и кладём их в отдельную папку вместе со следующим скриптом:
Код:
$sName = InputBox('Certificate name', 'Enter Certificate name:', @UserName)
If @error Then Exit

_MakeCert($sName, StringLower($sName))
MsgBox(64, @ScriptName, 'Finish')

Func _MakeCert($sName, $sPass)
    RunWait('makecert.exe -r -n "CN=' & $sName & '" -b 01/01/2013 -e 01/01/2099 -eku 1.3.6.1.5.5.7.3.3 -sv "' & $sName & '.pvk" "' & $sName & '.cer"', '', @SW_HIDE)
    RunWait('pvk2pfx.exe -pvk "' & $sName & '.pvk" -spc "' & $sName & '.cer" -pfx "' & $sName & '.pfx" -pi ' & $sPass & ' -po ' & $sPass, '', @SW_HIDE)
  
    FileDelete($sName & '.cer')
    FileDelete($sName & '.pvk')
EndFunc


Запускаем скрипт, вводим имя сертификата и нажимаем ОК, далее вводим пароль и ожидаем завершения.
Сертификат создан под введённым именем с расширением .pfx.
Желательно теперь этот сертификат импортировать в систему (запустите сам файл сертификата и следуйте указаниям).

Теперь чтобы подписать вашу скомпилированную программу используем следующий скрипт:
Код:
_Sign('Your\Exe\File.exe', 'Your\Certificate\File.pfx', 'CertPassword')

Func _Sign($sExeFile, $sCertFile, $sPass)
    RunWait('signtool.exe sign /f "' & $sCertFile & '" /p ' & $sPass & ' "' & $sExeFile & '"', '', @SW_HIDE)
    RunWait('signtool.exe timestamp /t "http://timestamp.globalsign.com/scripts/timstamp.dll" "' & $sExeFile & '"', '', @SW_HIDE)
EndFunc


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





Вариант №2:
Прикрепляю утилиту где это всё упрощено.
Используем ком. строку:

MyCert.exe /MakeCert -Name="MyCorp" -File="C:\MyCorp.pfx" -Pass="MyPass" -Import -Silent
* В тихом режиме создаст файл сертификата "C:\MyCert.pfx" используя имя "MyCorp", и пароль "MyPass", затем импортирует этот файл в систему

MyCert.exe /Sign -Exe="C:\MyProg.exe" -Cert="C:\MyCorp.pfx" -Pass="MyPass" -Silent
* В тихом режиме пропишет цифровую подпись исполняемому файлу "C:\MyProg.exe" используя файл сертификата "C:\MyCorp.pfx" с паролем "MyPass".

Запустите утилиту без параметров чтобы увидеть больше возможностей.
 

Вложения

  • MyCert.zip
    673.8 КБ · Просмотры: 13
Последнее редактирование:
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
Вопрос:
Как выполнить часть кода с повышением прав?

Ответ:
Есть два(?) варианта решения задачи...

1) Запускать часть кода с ShellExecute:
Код:
#pragma compile(AutoItExecuteAllowed, True) ;Это обязательно!!!

ShellExecute(@AutoItExe, '/AutoIt3ExecuteLine "MsgBox(0, 0, ''IsAdmin? '' & (IsAdmin() = 1))"', '', 'runas')


2) Запускать часть кода используя временный файл:
Код:
#pragma compile(AutoItExecuteAllowed, True) ;Это обязательно!!!

$sTmpFile = @TempDir & '\runas.tmp'

$sSrc = _
    '#RequireAdmin' & @CRLF & _
    'MsgBox(0, 0, "IsAdmin? " & (IsAdmin() = 1))' & @CRLF & _
    'FileDelete(@ScriptFullPath)'

$hFile = FileOpen($sTmpFile, 2)
FileWrite($hFile, $sSrc)
FileClose($hFile)

RunWait(@AutoItExe & ' /AutoIt3ExecuteScript "' & $sTmpFile & '"')
 
Статус
Закрыто для дальнейших ответов.
Верх