Orion9

|
Posted: Fri May 08, 2026 13:01 Post subject: |
|
|
AkulaBig
Вчера небольшую фигню написал.
Ошибка с доступом была не с FileWrite, а с FileAppend: именно эта функция предназначена для записи в конец файла, но через нее не удалось решить. Вероятно, есть какая-то проблема с правами, через BinaryFile получается же. В общем, это остается Loopback на заметку. Хотя если он вернётся, первым партийным заданием для него будет научить нейросеть работать с Autorun Тогда код можно будет тянуть, как с конвеера ) Возможно, этот код не будет полностью рабочим, но в качестве заготовок сойдет.
Сейчас для Autohotkey эта схема работает идеально. Достаточно просто спростить, и будет ответ:
 Hidden text | Code: | Использование WriteFile через DllCall — это отличный способ работать с файлами на низком уровне, минуя стандартные буферы AHK.Ниже полный пример для AutoHotkey v2, который открывает файл (или создает его), перемещает указатель в конец (для режима Append) и записывает текст.autohotkey; Настройки
FilePath := A_ScriptDir "\example_log.txt"
TextToWrite := "Новая строка лога через WriteFileW`r`n"
; 1. Открываем файл через CreateFileW
; Параметры: путь, доступ (0x40000000 = Generic_Write), общий доступ,
; создание (4 = Open_Always), флаги (128 = Normal), шаблон
hFile := DllCall("kernel32.dll\CreateFileW",
"Str", FilePath, ; lpFileName
"UInt", 0x40000000, ; dwDesiredAccess (GENERIC_WRITE)
"UInt", 7, ; dwShareMode (Read/Write/Delete)
"Ptr", 0, ; lpSecurityAttributes
"UInt", 4, ; dwCreationDisposition (OPEN_ALWAYS)
"UInt", 128, ; dwFlagsAndAttributes (NORMAL)
"Ptr", 0, "Ptr") ; hTemplateFile
if (hFile = -1) {
MsgBox "Не удалось открыть файл. Ошибка: " A_LastError
return
}
; 2. Перемещаем указатель в конец файла (режим Append)
; Параметры: дескриптор, смещение, направление (2 = FILE_END)
DllCall("kernel32.dll\SetFilePointer", "Ptr", hFile, "Int", 0, "Ptr", 0, "UInt", 2)
; 3. Подготовка данных для записи (Unicode)
; Рассчитываем размер строки в байтах (длина * 2 для UTF-16)
BytesToWrite := StrLen(TextToWrite) * 2
Written := Buffer(4, 0) ; Буфер для получения количества записанных байт
; 4. Запись в файл через WriteFile
Success := DllCall("kernel32.dll\WriteFile",
"Ptr", hFile, ; hFile
"Str", TextToWrite, ; lpBuffer
"UInt", BytesToWrite, ; nNumberOfBytesToWrite
"Ptr", Written.Ptr, ; lpNumberOfBytesWritten
"Ptr", 0) ; lpOverlapped
; 5. Закрываем дескриптор (обязательно!)
DllCall("kernel32.dll\CloseHandle", "Ptr", hFile)
if Success
MsgBox "Данные успешно добавлены в файл."
else
MsgBox "Ошибка записи: " A_LastError
Используйте код с осторожностью.Что здесь происходит:CreateFileW: Ключевой момент — флаг OPEN_ALWAYS (4). Он открывает файл, если он есть, или создает новый, если его нет.SetFilePointer: Без этой функции WriteFile просто перезапишет начало файла. Флаг FILE_END (2) прыгает в самый конец, реализуя логику Append.Unicode: Мы используем WriteFile и передаем строку напрямую. Поскольку AHK v2 работает в Unicode, StrLen * 2 дает точный размер данных в байтах.CloseHandle: Если забыть закрыть дескриптор, файл будет "заблокирован" системой до завершения работы скрипта.⚠️ Важное замечание по кодировкеЭтот метод записывает "чистый" UTF-16 (Little Endian), который использует Windows. Если вам нужен UTF-8 или файл с BOM-меткой, перед записью строку нужно конвертировать через StrPut в отдельный буфер.Нужно ли добавить в пример конвертацию в UTF-8 перед записью?
Чтобы корректно работать с файлом, который может быть открыт другим процессом, нужно правильно настроить параметр dwShareMode в функции CreateFileW.В предыдущем примере мы использовали 7, что является комбинацией всех прав доступа. Вот как это работает:🛠 Ключевой параметр: dwShareModeЕсли другой процесс уже открыл файл, ваш DllCall вернет ошибку -1, если вы не укажете те же (или более широкие) права совместного доступа, которые запросил первый процесс.FILE_SHARE_READ (1): Разрешить другим читать файл.FILE_SHARE_WRITE (2): Разрешить другим писать в файл.FILE_SHARE_DELETE (4): Разрешить другим удалять или переименовывать файл.Итог (7): Максимальная совместимость.Обновленный код (AHK v2) с проверкой занятостиautohotkeyFilePath := A_ScriptDir "\shared_log.txt"
Text := "Запись в потенциально открытый файл`r`n"
; Пытаемся открыть файл с полным совместным доступом (7)
hFile := DllCall("kernel32.dll\CreateFileW",
"Str", FilePath,
"UInt", 0x40000000, ; GENERIC_WRITE
"UInt", 7, ; dwShareMode: READ(1) | WRITE(2) | DELETE(4)
"Ptr", 0,
"UInt", 4, ; OPEN_ALWAYS
"UInt", 128, ; NORMAL
"Ptr", 0, "Ptr")
if (hFile = -1) {
ErrorNum := A_LastError
if (ErrorNum = 32) ; ERROR_SHARING_VIOLATION
MsgBox "Файл жестко заблокирован другим процессом (Sharing Violation)."
else
MsgBox "Ошибка открытия: " ErrorNum
return
}
; Блокировка области файла (опционально)
; Чтобы два процесса не писали одновременно в одну точку,
; можно временно заблокировать хвост файла через LockFileEx.
; Переходим в конец
DllCall("kernel32.dll\SetFilePointer", "Ptr", hFile, "Int", 0, "Ptr", 0, "UInt", 2)
; Запись
Written := Buffer(4, 0)
DllCall("kernel32.dll\WriteFile", "Ptr", hFile, "Str", Text, "UInt", StrLen(Text)*2, "Ptr", Written.Ptr, "Ptr", 0)
; Закрываем
DllCall("kernel32.dll\CloseHandle", "Ptr", hFile)
Используйте код с осторожностью.⚠️ Ограничения "Совместного доступа"Жесткая блокировка: Если другой процесс открыл файл с dwShareMode := 0, вы не сможете открыть его даже для чтения, пока тот процесс не закроет дескриптор. Тут WinAPI бессилен, нужно только ждать.Смещение указателя: Когда два процесса пишут в конец одного файла одновременно, может возникнуть ситуация "гонки".Решение: Используйте специальное смещение 0xFFFFFFFF в OVERLAPPED структуре или SetFilePointer непосредственно перед записью.Сетевые папки: На сетевых дисках блокировки могут работать с задержкой из-за кеширования Windows.💡 СоветЕсли вы часто сталкиваетесь с ошибкой 32 (Sharing Violation), попробуйте обернуть CreateFileW в небольшой цикл Loop с Sleep 100 и 5–10 попытками. Это стандартная практика для работы с логами, которые могут быть заняты антивирусом или си |
А вот если бы он сразу выдавал готовый код, насколько это облегчило бы жизнь
 Hidden text | Code: | Func AppendToFile(FilePath, TextToWrite)
FilePath = TEMP "\example_log.txt"
TextToWrite = "Новая строка лога через WriteFileW`r`n"
Static dwShareMode = 7 # Read/Write/Delete
Static GENERIC_WRITE = 0x40000000, OPEN_ALWAYS = 4, NORMAL = 128
hFile := DllCall("kernel32.dll\CreateFileW", _
"Str", FilePath, _
"UInt", GENERIC_WRITE, _
"UInt", dwShareMode, _
"Ptr", 0, _
"UInt", 4, _
"UInt", 128, _
"Ptr", 0, "Ptr")
If hFile = -1 Then Return -1
# перемещение указателя в конец файла
# дескриптор, смещение, направление (2 = FILE_END)
DllCall("kernel32.dll\SetFilePointer", "Ptr", hFile, "Int", 0, "Ptr", 0, "UInt", 2)
# размер строки в байтах (длина * 2 для UTF-16)
BytesToWrite = StrLen(TextToWrite) * 2
Written = 0
Success = DllCall("kernel32.dll\WriteFile", _
"Ptr", hFile, _
"WStr", TextToWrite, _
"UInt", BytesToWrite, _
"UInt*", @Written, _
"Ptr", 0)
DllCall("kernel32.dll\CloseHandle", "Ptr", hFile)
If Success Then
Return 0
Else
Return -2
EndIf
EndFunc |
Я хотел было доделать этот пример до конца, но потом понял, что для работы с файлом ExifTool строку для записи надо еще в UTF-8 переводить. А объект BinaryFile сам это делает. В общем, решил в итоге перевести его в статик, чтобы он не создавался при каждом вызове, это должно немного поднять скорость. Сейчас весь фрагмент, очищенный от комментариев, выглядит так
| Code: | If FileExist(gExifOut) Then
DllCall("kernel32.dll\DeleteFileW", "Wstr", gExifOut)
EndIf
Local sArgs = "-G0" & auCRLF & _
"-lang" & auCRLF & _
"ru" & auCRLF & _
"-W+!" & auCRLF & gExifOut & auCRLF & FileName & auCRLF & "-execute" & auCRLF
Static obj = BinaryFile(gExifArgs, "a")
obj.WriteStr(sArgs, "", "UTF-8")
ansi = "<time-out>"
For i = 1 To 50
If FileExist(gExifOut) Then
ansi = FileRead(gExifOut, 0, "UTF-8")
DllCall("kernel32.dll\DeleteFileW", "Wstr", gExifOut)
Break
EndIf
Sleep(20)
Next
Return ansi |
Работает значительно лучше и стабильнее, но все-равно могут прошмыгивать пустые позиции, хоть и мало.
| AkulaBig wrote: | | Зато теперь все понятно. Проблема Ауторан в том, что он почему-то выводит данные не для каждого файла, а для всех сразу. |
Причем это характерно только для 64-битной версии, в 32-битной такого нет. Об этом я в свое время писал Loopback, но он тогда не ответил. Кроме того, в обеих версиях при прокрутке панели ползунком повляются белые прямоугольные артефакты и начинается какая-та "дичь". В общем, есть проблемы. Очень всё сыро выглядит.
| AkulaBig wrote: | | Break отлично работает. Нет файла, создает его. Есть файл, очищает его. |
Хех. Не подвёл болван!
| AkulaBig wrote: | | Осталось только проверить работу с au3. Вдруг там что-то -stay_open изменит. |
Изменит однозначно. Но я бы vbs не скидывал со счетов. И сильно по поводу отключения VBScript на некоторых машинах не парился. На таких компах и сборкам делать нечего. |
|