Autorun

Обсуждение только плагинов и утилит для Total Commander.

Модераторы: Avada, CaptainFlint, Nik, d-view, Моторокер

AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Оказывается даже после исправления работы макроса AUTORUN_TCLANG, макрос AUTORUN_TCLANGFILE остается весьма полезным. Только с помощью него можно отследить переключение на внутренний язык в ТС. В этом случае AUTORUN_TCLANG все равно показывает язык ENG, а AUTORUN_TCLANGFILE пустое значение.
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Orion9
Наконец-то дошли руки опробовать ваш вариант с type:

Код: Выделить всё

Func SwitchIniSect(sIni, sSect, sLng)

	arrLang = List("eng", "rus")
	Local TextFile, i
	Local arrFile = List()
	ProcessExecGetOutput TextFile %COMSPEC% '/c type "%sIni%"'
	arrFile.Text = TextFile

	For i = 0 To arrLang.Count-1
		If StrPos(arrFile.Text, "[" & sSect & "_" & arrLang._Item[i] & "]") = 0 Then
			arrFile.Text = StrReplace(arrFile.Text, "[" & sSect & "]", "[" & sSect & "_" & arrLang[i] & "]")
			Break
		EndIf
	Next

Local ResTextFile = arrFile.Text

# ResTextFile должен быть файл
	ShellExec /SW_HIDE /WAIT %COMSPEC% '/c type %ResTextFile% > %sIni%'
# ResTextFile какой синтаксис?
	ShellExec /SW_HIDE /WAIT %COMSPEC% '/c call echo ResTextFile > %sIni%'

	Free(arrLang)
	Free(arrFile)
EndFunc
Да, файл в переменную считал. А вот как из переменной данные в файл записать, непонятно. Ну и данные в Win1251, а надо в юникоде.
Loopback
Для полноценной работы с файлом необходимо три команды:
FileOpen, FileWrite, FileClose. Ранее вы писали, что пока не планируете реализовывать работу с файлами, но может все-же дойдут у вас до этого руки. Ведь весь остальной функционал для работы с файлами есть.
Loopback
Сообщения: 1646
Зарегистрирован: Пн сен 07, 2009 09:29

Сообщение Loopback »

AkulaBig писал(а):что пока не планируете реализовывать работу с файлами
Работа с файлами всегда была в приоритетном списке. Не хотелось делать непродуманное решение, а то потом менять сложно.
AkulaBig писал(а):FileOpen, FileWrite, FileClose
Т.е. FileRead не нужна? :wink:
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Я про минимальные хотелки. FileRead это уже будет полный набор. Это очень хорошо.
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

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

Раньше приходилось пользоваться жёстким костылем - записывать переменную в буфер обмена и сохранять содержимое на диск с помощью vbs-скрипта. Но это было до того, как в Autorun появилась функция DllCall. Сейчас, наверное, всё можно решить через Win API.

К сожалению, у меня сейчас не те возможности, чтобы во всём детально разобраться, но поскольку я давно хотел сделать такой вызов, то глянул буквально одним глазком:
https://learn.microsoft.com/en-us/windo ... g-to-files

Код: Выделить всё

RegisterCommand 60017 "FileWriteTest"
RegisterCommand 60018 "AssocIniWriteTest"

Func FileWriteTest(lParam)
    Local Const GENERIC_WRITE = 0x40000000, CREATE_ALWAYS = 2, FILE_ATTRIBUTE_NORMAL = 128
    Local bytes = 0
    Local file = COMMANDER_PATH & "\Ini\WriteTest.ini"
    Local txt
    txt = "Reading From and Writing to Files" & auCRLF & auCRLF & _
	  "An application reads from and writes to a file by using the ReadFile, ReadFileEx, WriteFile, and WriteFileEx functions." & auCRLF & auCRLF & _	  
	  "These functions require a handle to a file to be opened for reading and writing, respectively. They read and write a specified number of bytes at the location indicated by the file pointer. The data is read and written exactly as specified; the functions do not format the data."
    Local buf = Buffer(StrLen(txt))
    buf.SetStr(txt, 0,"","ANSI")
    Local nBytes = StrLen(txt)    
    #Local nBytes = StrLen(txt) * 2
    Local hFile = DllCall("CreateFileW", "wstr", file, "dword", GENERIC_WRITE, "dword", 0, "ptr", 0, "dword", 2, "dword", 128, "ptr", 0, "handle")
    #MsgBox("Handle " & hFile)
    DllCall("WriteFile", "handle", hFile, 'ptr', buf.ptr, 'dword', buf.size, "dword*", @bytes, 'ptr', 0)     
    #DllCall("WriteFile", "handle", hFile, 'wstr', txt, 'dword', nBytes, "dword*", @bytes, 'ptr', 0)
    DllCall("CloseHandle", "handle", hFile)
    MsgBox("Buffer size " & buf.size & auCRLF & "Bytes written " & bytes)    
EndFunc

Func AssocIniWriteTest(lParam)
    Local Const GENERIC_WRITE = 0x40000000, CREATE_ALWAYS = 2, FILE_ATTRIBUTE_NORMAL = 128
    Local bytes = 0
    Local ini = COMMANDER_PATH & "\Ini\Associations.ini"
    Local file = COMMANDER_PATH & "\Ini\WriteTest.ini"
    Local out
    ProcessExecGetOutput out %COMSPEC% '/c type "%ini%"'
    If StrPos(out, '[-Associations]') > 0 Then
       out = StrReplace(out, '[-Associations]', '[Associations]')
    Else
       out = StrReplace(out, '[Associations]', '[-Associations]')    
    Endif 
    Local nBytes = StrLen(out)
    Local hFile = DllCall("CreateFileW", "wstr", file, "dword", GENERIC_WRITE, "dword", 0, "ptr", 0, "dword", CREATE_ALWAYS, "dword", FILE_ATTRIBUTE_NORMAL, "ptr", 0, "handle")
    DllCall("WriteFile", "handle", hFile, 'str', out, 'dword', nBytes, "dword*", @bytes, 'ptr', 0)
    DllCall("CloseHandle", "handle", hFile)
    MsgBox("Handle " & hFile & auCRLF & "Bytes written " & bytes)    
EndFunc

Код: Выделить всё

TOTALCMD#BAR#DATA
60017

%COMMANDER_EXE%
Тест записи текста в файл

1
-1

Код: Выделить всё

TOTALCMD#BAR#DATA
60018

%COMMANDER_EXE%
Тест переключения Associations.ini в WriteTest.ini

1
-1
В первой функции я эксперементировал с объектом buffer, но он здесь в принципе не нужен, если только не нужно сменить кодировку текста на UTF-8 или что-то такое же специфическое. Для ANSI работает такой вызов:

Код: Выделить всё

DllCall("WriteFile", "handle", hFile, 'str', txt, 'dword', nBytes, "dword*", @bytes, 'ptr', 0)
Если нужен UTF-16, то понадобится в 2 раза больше байт и тип данных 'wstr':

Код: Выделить всё

Local nBytes = StrLen(txt) * 2
DllCall("WriteFile", "handle", hFile, 'wstr', txt, 'dword', nBytes, "dword*", @bytes, 'ptr', 0)
Вторая функция переключает секцию ассоциаций, но результат переключения пишет в другой файл, а не в Associations.ini. Опять же, если нужен Unicode, то должен быть "wstr" и удвоенное количество байт для записи.

Вообщем, можете попробовать, если срочно нужно. Но думаю, Loopback уже настроился на большой бонус в новой версии )
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Orion9 писал(а):попробовать, если срочно нужно
Совершенно не срочно. Наоборот, закончил блок настроек и решил разобраться с type.
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

AkulaBig
Если файл - обычный текстовый небольшой файл, то type хорошо подходит для считывания. Но DllCall - это ключ от всех дверей. Будь у меня возможность, я бы глубже изучил именно DllCall и Win API. Не так уж это и сложно, нужно просто всё пробовать и смотреть, как работают различные параметры вызова с различными флагами и аттрибутами. Единственный минус: это отнимает много времени и усилий, что, разумеется, не всегда можно себе позволить.
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Orion9 писал(а):DllCall и Win API
То-есть мы можем подключать функции WinAPI? Офигеть!!!
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

AkulaBig писал(а):Офигеть!!!
Так и есть. Теперь вся Windows у наших ног )

Можно добавить, например, собственных звуковых событий в ТС через вызов функции PlaySound:

Код: Выделить всё

DllCall("Winmm.dll\PlaySoundW", "wstr", "C:\Windows\Media\tada.wav",  "ptr", 0, "dword", 131072 + 1)
Понятно, что такого же эффекта можно добиться и обычным скриптом:

Код: Выделить всё

Set oFS = CreateObject("SAPI.SpFileStream")
oFS.Open "C:\Windows\Media\tada.wav"
oFS.Close
Но разве не круто, сделать то же самое самим Autorun при помощи DllCall?

И ведь это только простенький пример, а перечень возможностей действительно огромен.
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Orion9 писал(а):Можно добавить, например, собственных звуковых событий в ТС через вызов функции PlaySound:
Этот пример я уже выше видел. И в справке примеры видел. Но вот как прикрутить функции для работы с файлом. Пока не совсем понятно.

Добавлено спустя 1 час 40 минут:

Попробовал открыть файл:
hHandle = DllCall("CreateFile" , sIni, 511, 0, 0, 0, 0, 0)
Пустое значение возвращает. В AutiIt тоже есть функция DllCall. Попытаться к ней что-ли присер поискать.

Добавлено спустя 8 минут:

Надо ведь для значения тип писать. Попробую переделать.

Добавлено спустя 32 минуты:

А ведь получилось:

Код: Выделить всё

	Local hHandle = DllCall("CreateFile", _
							"wstr", sIni, _
							"int", 0x80000000 Or 0x40000000, _
							"int", 0, _
							"ptr", 0, _
							"int", 3, _
							"int", 0x00000080, _
							"int", 0)
Добавлено спустя 40 минут:

Интересно, чем заменить в

Код: Выделить всё

DllCall("kernel32.dll", "int", "ReadFile", "hwnd", $hand, "ptr*", DllStructGetPtr($strData), "int", 32, "long_ptr", 0, "int", 0)
DllStructGetPtr($strData)?
Orion9
Это на ahk. Можете DllStructGetPtr($strData) перевести на Autorun?
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

AkulaBig
Если завтра будет возможность, то посмотрю на счет ReadFile, но эта функция менее важна, т.к. сейчас считывать можно и через type. Именно поэтому, пока мне предоставилась возможность, я сосредоточился на WriteFile.

В принципе, все должно работать, если к вашему примеру добавить после строки:

Код: Выделить всё

Local ResTextFile = arrFile.Text
Что-то вроде:

Код: Выделить всё

Local nBytes = StrLen(ResTextFile)
Local Const GENERIC_WRITE = 0x40000000, CREATE_ALWAYS = 2, FILE_ATTRIBUTE_NORMAL = 128
Local bytes = 0
Local hFile = DllCall("CreateFileW", "wstr", sIni, "dword", GENERIC_WRITE, "dword", 0, "ptr", 0, "dword", CREATE_ALWAYS, "dword", FILE_ATTRIBUTE_NORMAL, "ptr", 0, "handle")
If hFile > 0 Then
   DllCall("WriteFile", "handle", hFile, 'str', ResTextFile, 'dword', nBytes, "dword*", @bytes, 'ptr', 0)
   DllCall("CloseHandle", "handle", hFile)
Endif
Но нужно проверить и посмотреть на практике. Хотя и другие проверки в коде не помешали бы, кроме проверки хэндла, а лучше бэкап файла перед записью.
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Orion9 писал(а):сейчас считывать можно и через type
Не катит совершенно. Ведь type считывает в ANSI, а надо в Юникод.

Код: Выделить всё

Local hFile = DllCall("CreateFileW", "wstr", sIni, "dword", GENERIC_WRITE, "dword", 0, "ptr", 0, "dword", CREATE_ALWAYS, "dword", FILE_ATTRIBUTE_NORMAL, "ptr", 0, "handle")
У вас пропущен параметр после "handle". Собственно рабочий CreateFile я выше привел. Хотя, возможно с параметрами и придется поиграться.
А насчет ReadFile пришла идея. Надо примеры смотреть на win api, как буфер создавать. Понятно, что на autoit и ahk они применяют существующую функцию, но должен быть и другой вариант.
Интересно, что в TCIMG есть функция DllStructGetPtr.
'str', ResTextFile - как-раз это не работает. Здесь ссылка должна быть.
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

Не катит совершенно. Ведь type считывает в ANSI, а надо в Юникод.
Да, совсем забыл, что нужен именно Юникод. Но обойти можно, если использовать ключ /u. Вот полный пример чтения и записи файла в юникоде с использованием type:

Код: Выделить всё

RegisterCommand 60018 "TypeUnicodeTest"

Func TypeUnicodeTest(lParam)
    Local Const GENERIC_WRITE = 0x40000000, CREATE_ALWAYS = 2, FILE_ATTRIBUTE_NORMAL = 128
    Local bytes = 0
    Local sIni = COMMANDER_PATH & "\Ini\Start.ini"
    Local out
    ProcessExecGetOutput /C:1200 out %COMSPEC% '/u /c type "%sIni%"'
    If StrPos(out, '[user_eng]') > 0 Then
       out = StrReplace(out, '[user_eng]', '[user]')
    Else
       out = StrReplace(out, '[user]', '[user_eng]')    
    Endif     
    Local nBytes = StrLen(out) * 2
    Local hFile = DllCall("CreateFileW", "wstr", sIni, "dword", GENERIC_WRITE, "dword", 0, "ptr", 0, "dword", CREATE_ALWAYS, "dword", FILE_ATTRIBUTE_NORMAL, "ptr", 0, "handle")
    If hFile > 0 Then
       Local bom = Chr(0xFEFF)
       DllCall("WriteFile", "handle", hFile, 'wstr', bom, 'dword', 2, "dword*", @bytes, 'ptr', 0)    
       DllCall("WriteFile", "handle", hFile, 'wstr', out, 'dword', nBytes, "dword*", @bytes, 'ptr', 0)
       DllCall("CloseHandle", "handle", hFile)
    Endif
EndFunc
У вас пропущен параметр после "handle". Собственно рабочий CreateFile я выше привел.
Не знаю, что может быть не так с моим хэндлом, но всё работает :) Только маркер BOM обязательно должен быть записан в начало, иначе type читает файл как ANSI, не смотря на ключ /u.

Ini-файл с нашим многострадальным Майклом Тело )
[cut="Start.ini"][user]
menu1=-Помощь
menu2=Справка 9.51 (ru)
cmd2=%COMMANDER_PATH%\Help\Wcmd_rus.chm
menu3=Сочетания клавиш 10.52 (ru)
cmd3=%COMMANDER_PATH%\Help\TC.Hotkeys.chm
menu4=Все команды 10.52 (ru)
cmd4=%COMMANDER_PATH%\Help\TC.Commands.chm
menu5=История версий 3.00-9.51 (ru)
cmd5=%COMMANDER_PATH%\Help\Hist_rus.txt
menu6=RU.TCKB 1.7 (ru)
cmd6=%COMMANDER_PATH%\Help\RU.TCKB.chm
menu7=--
menu8=-Michel Teló - Ai Se Eu Te Pego.mp4
menu9=Официальный сайт
cmd9=https://www.ghisler.com/
menu10=Русский сайт
cmd10=http://wincmd.ru/
menu11=Русский форум
cmd11=http://forum.wincmd.ru/[/cut]
Специально туда вставил Michel Teló для теста. Файл на самом деле гораздо больше, это только его начальная часть. Код кнопки для теста:

Код: Выделить всё

TOTALCMD#BAR#DATA
60018

%COMMANDER_EXE%
Тест переключения секции

1
-1
Ну, и как обещал ReadFile:

Код: Выделить всё

RegisterCommand 60019 "ReadFileTest"

Func ReadFileTest(lParam)
    Local Const GENERIC_READ = 0x80000000, FILE_SHARE_READ = 1, OPEN_EXISTING = 3, FILE_ATTRIBUTE_NORMAL = 128
    Local bytes = 0
    Local sIni = COMMANDER_PATH & "\Ini\Start.ini"
    Local fs = FileGetSize(sIni)    
    Local buf = Buffer(fs)
    buf.Zero()
    Local hFile = DllCall("CreateFileW", "wstr", sIni, _
                                         "dword", GENERIC_READ, _
                                         "dword", FILE_SHARE_READ, _
                                         "ptr", 0, _
                                         "dword", OPEN_EXISTING, _
                                         "dword", FILE_ATTRIBUTE_NORMAL, _
                                         "ptr", 0, _
                                         "handle")               
    If hFile > 0 Then
       DllCall("ReadFile", "handle", hFile, 'ptr', buf.ptr, 'dword', fs, "dword*", @bytes, 'ptr', 0)       
       DllCall("CloseHandle", "handle", hFile)       
    Endif
    Local out = buf.GetStr(0,"","")        
    MsgBox("bytes " & bytes & auCRLF & out)
EndFunc
Все работает, но нужно больше тестов и проверок в коде.

Код: Выделить всё

TOTALCMD#BAR#DATA
60019

%COMMANDER_EXE%
Тест чтения

1
-1
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Orion9 писал(а):Все работает
Есть проблемы. Если читать файл через type, все работает отлично:

Код: Выделить всё

	ProcessExecGetOutput /C:1200 out %COMSPEC% '/u /c type "%sIni%"'
......
		Local bom = Chr(0xFEFF)
		DllCall("WriteFile", "handle", hFile, 'wstr', bom, 'dword', 2, "dword*", @bytes, 'ptr', 0)
А через ReadFile в конец файла записывается какой-то мусор. Выкладываю весь код:

Код: Выделить всё

Func SwitchIniSect(sIni, sSect, sLng)

	Local arrLang = List("eng", "rus")
	Local i

	Local Const GENERIC_READ = 0x80000000
	Local Const FILE_SHARE_READ = 1
	Local Const OPEN_EXISTING = 3
	Local Const FILE_ATTRIBUTE_NORMAL = 128
	Local bytes = 0
	Local fs = FileGetSize(sIni)
	Local buf = Buffer(fs)
	buf.Zero()
	# Local hFile = DllCall("CreateFileW", "wstr", sIni, _
	# 									 "dword", GENERIC_READ, _
	# 									 "dword", FILE_SHARE_READ, _
	# 									 "ptr", 0, _
	# 									 "dword", OPEN_EXISTING, _
	# 									 "dword", FILE_ATTRIBUTE_NORMAL, _
	# 									 "handle", 0)
	# If hFile > 0 Then
	# 	DllCall("ReadFile", "handle", hFile, _
	# 						"ptr", buf.ptr, _
	# 						"dword", fs, _
	# 						"dword*", @bytes, _
	# 						"ptr", 0)
	# 	DllCall("CloseHandle", "handle", hFile)
	# Endif
	# Local out = buf.GetStr(0,"","")
	# MsgBox(out)

	ProcessExecGetOutput /C:1200 out %COMSPEC% '/u /c type "%sIni%"'

	# Переименовываем все секции по шаблону [sect_lng]
	# Rename all sections according to template [sect_lng]
	For i = 0 To arrLang.Count-1
		If StrPos(out, "[" & sSect & "_" & arrLang._Item[i] & "]") = 0 Then
			out = StrReplace(out, "[" & sSect & "]", "[" & sSect & "_" & arrLang[i] & "]")
			# MsgBox("[" & sSect & "]", "[" & sSect & "_" & arrLang[i] & "]")
			Break
		EndIf
	Next

	# Переименовываем оригинальную секции по шаблону [sect]
	# Rename original section according to template [sect]
	If StrPos(out, "[" & sSect & "_" & sLng & "]", 1) Then
		out = StrReplace(out, "[" & sSect & "_" & sLng & "]", "[" & sSect & "]")
	Endif

	Local Const GENERIC_WRITE = 0x40000000
	Local Const CREATE_ALWAYS = 2
	bytes = 0
	Local nBytes = StrLen(out) * 2
	Local hFile = DllCall("CreateFileW", "wstr", sIni, _
										 "dword", GENERIC_WRITE, _
										 "dword", 0, _
										 "ptr", 0, _
										 "dword", CREATE_ALWAYS, _
										 "dword", FILE_ATTRIBUTE_NORMAL, _
										 "handle", 0)
	If hFile > 0 Then

		Local bom = Chr(0xFEFF)
		DllCall("WriteFile", "handle", hFile, 'wstr', bom, 'dword', 2, "dword*", @bytes, 'ptr', 0)

		DllCall("WriteFile", "handle", hFile, _
							 "wstr", out, _
							 "dword", nBytes, _
							 "dword*", @bytes, _
							 "ptr", 0)
		DllCall("CloseHandle", "handle", hFile)
	Endif

	Free(arrLang)
EndFunc
В этом варианте вышеприведенный код комментим. Комментарии снимаем.
Вот мусор: digit months." & auCRLF &
Кстати, я привел синтаксис функций к оригинальному. Но это никак не повлияло. Мусор все равно записывается.
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Подключил все инишники, которые необходимо изменять. Мусор во всех файлах разный. А в некоторых его нет. Скорей всего проблемы с буфером.

Добавлено спустя 1 час 17 минут:

Устранил косячок:
Local fs = FileGetSize(sIni) + 1 или 2. Зависит от чего-то.
Видимо вариант с type лучше.
Последний раз редактировалось AkulaBig Пн мар 31, 2025 11:14, всего редактировалось 1 раз.
Ответить