Страница 179 из 243

Добавлено: Пн мар 31, 2025 10:59
Loopback
AkulaBig писал(а):Скорей всего проблемы с буфером.
Думаю да. При такой записи

Local out = buf.GetStr(0,"","")

когда второй параметр = 0, GetStr должна искать нулевой символ до конца буфера. Но после чтения файла весь буфер заполнен текстовыми данными, так что нулевого символа там точно нет. Просто в GetStr есть забытая недоделка, в результате чего она при поиске нулевого символа может выйти за границы буфера.

Но полагаю, в данном случае правильнее будет не завязываться на поиск нуля, поскольку он и не предполагается в текстовых данных, а явно указывать байтовый размер строки:

Local out = buf.GetStr(0,bytes)

Добавлено: Пн мар 31, 2025 12:04
Orion9
AkulaBig писал(а):Local fs = FileGetSize(sIni) + 1 или 2. Зависит от чего-то.
Видимо вариант с type лучше.
Забыл уточнить, что BOM нужно добавлять, только когда используется type, поскольку она выкидывает его из текста при считывании файла. Если использовать ReadFile, то BOM добавлять не нужно, т.к. эта функция считывает весь файл целиком вместе с BOM.
Loopback писал(а):Просто в GetStr есть забытая недоделка, в результате чего она при поиске нулевого символа может выйти за границы буфера.
Ну, я-то как раз и опирался на то, что выхода за границы буфера при его достижении не будет, поэтому и не стал связываться с нулевым байтом, хотя Microsoft советует делать именно так :)

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

// Read one character less than the buffer size to save room for
    // the terminating NULL character. 

    if( FALSE == ReadFileEx(hFile, ReadBuffer, BUFFERSIZE-1, &ol, FileIOCompletionRoutine) )
https://learn.microsoft.com/en-us/windo ... or-writing

AkulaBig, вызов, который вы используете, не совсем правильный:

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

Local hFile = DllCall("CreateFileW", "wstr", sIni, _
                               "dword", GENERIC_WRITE, _
                               "dword", 0, _
                               "ptr", 0, _
                               "dword", CREATE_ALWAYS, _
                               "dword", FILE_ATTRIBUTE_NORMAL, _
                               "handle", 0)
В моем вызове последний "handle" (без значения за ним) указывает тип возвращаемого значения от функции CreateFileW. В вашем же вызове тип возвращаемого значения опущен, а значит по умолчанию он считается int, что в будущем может привести к проблемам, особенно в 64-битном ТС:
DllCall писал(а):ResultType
Тип результата вызываемой функции. Возможные типы перечислены в разделе «Поддерживаемые типы». Если параметр не задан, используется тип int.

Добавлено: Пн мар 31, 2025 12:34
AkulaBig
Loopback писал(а):Local out = buf.GetStr(0,bytes)
А вот так никакого мусора! Конечно еще потестю, но заранее спасибо.
Orion9 писал(а):BOM нужно добавлять, только когда используется type
Это я быстро понял. Весьма занимательный глюк получился. Секция как-бы есть но не работает и CodeFold в Акельке ее не видит. При этом она не подсвечивается в том-же Акельке. То-есть байт или два добавлял без учета ее. Собственно проблему уже решили.
Orion9 писал(а):Ну, я-то как раз и опирался на то, что выхода за границы буфера при его достижении не будет, поэтому и не стал связываться с нулевым байтом
Видимо поэтому и результат не скидывался в ноль, как это описано у майки для FileRead. Для интереса надо будет проверить сейчас.
Orion9 писал(а):В моем вызове последний "handle" (без значения за ним) указывает тип возвращаемого значения от функции CreateFileW.
Хитро. Я привел к спецификации майки. Думал может в этом проблема. Вернусь к вашему варианту. Ваши знания не соответствуют ни WinAPI, ни au3, ни ahk. Там все типы и значения соответствуют друг-другу. Где вы такого набрались-то? :lol:
Проверил, bytes из моего кода все равно показывает размер файла или буфера, но в ноль не скидывается. Вроде по описанию майки должен.

Добавлено: Пн мар 31, 2025 18:43
AkulaBig
Решил полностью собрать вариант и на type. Возник страшнейший глюк. Долго ковырял, пока не понял, что у type буфер м-а-аленький. А у меня файл Searches.ini 124 848 байт.
А вот через ReadFile все прекрасно работает. С другой стороны возник очень интересный вопрос. А как такой файл проглотил объект Buffer?
При дальнейшем тестировании выяснилось, что если Searches.ini перевести в ANSI, они и с ReadFile перестает работать. Но хотя-бы не обрезается.

Добавлено: Пн мар 31, 2025 19:02
Orion9
AkulaBig писал(а):Где вы такого набрались-то?
Ну как где. Известно где - на танцполе, где танцуют с бубнами :lol:

Я уже [s]жаловался[/s] писал несколько страниц назад, что самая большая сложность при работе с WinAPI и DllCall - множество неочевидных и неоднозначных типов данных. Разобраться в этом не так просто, тем более, что я не занимаюсь профессиональным программированием и не держу под рукой IDE, SDK и прочие инструменты разработчиков.

Остаётся добывать информацию по крупицам и складывать со временем в мозаику. Применительно к вашему вопросу, нет никакой разницы между:

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

Local hFile = DllCall("CreateFileW", "wstr", FileName, _
                                         "dword", GENERIC_READ, _
                                         "dword", FILE_SHARE_READ, _
                                         "ptr", 0, _
                                         "dword", OPEN_EXISTING, _
                                         "dword", FILE_ATTRIBUTE_NORMAL, _
                                         "ptr", 0, _
                                         "handle")   

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

Local hFile = DllCall("CreateFileW", "wstr", FileName, _
                                         "dword", GENERIC_READ, _
                                         "dword", FILE_SHARE_READ, _
                                         "handle", 0, _
                                         "dword", OPEN_EXISTING, _
                                         "dword", FILE_ATTRIBUTE_NORMAL, _
                                         "handle", 0, _
                                         "handle")   
Я просто выбрал в пользу ptr, потому что у меня отложился в голове фрагмент из справки Autorun:
Передать нулевой указатель (NULL) с помощью данного типа нельзя. При необходимости передавайте 0 с помощью типа ptr.
Но в нём говорится о типе str/wstr, т.к. можно считать, что это моя мисинтерпретация :)
AkulaBig писал(а):если Searches.ini перевести в ANSI, они и с ReadFile перестает работать.
А вы wstr на str при этом заменили?

Добавлено: Пн мар 31, 2025 19:16
AkulaBig
Orion9 писал(а): по крупицам
Подобное я читал. Кстати, DllCall даже в TCIMG есть. Я и не знал раньше.
Но дело не только в типах данных. Вы ведь и формат изменили. Параметра "handle") просто нет в спецификации. А это уже высший пилотаж.
Orion9 писал(а):А вы wstr на str при этом заменили?
Последний тест я уже просто на вашем примере выполнял с предыдущей странице. А у вас тоже wstr. Надо будет попробовать. А не, out сразу обрезается. Уже во WriteFile поступает обрезанный файл.

Добавлено: Пн мар 31, 2025 22:00
Loopback
AkulaBig писал(а): Долго ковырял, пока не понял, что у type буфер м-а-аленький.
Это не у type, а ограничение функций плагинов. Они не могут возвращать более 32Кб данных. Наследие от ранних версий плагина, когда 32Кб должно было хватить всем :? Тупо увеличивать буфер плохой вариант, надо просто реализовать механизм выделения памяти со стороны плагина. Но собственно, столкнуться с этой проблемой кроме одной этой функции вряд ли где получится, так что не считаю проблему критической, особенно в свете наличия альтернатив. Но в планах на доработку она есть, только чуть позже.
AkulaBig писал(а):А как такой файл проглотил объект Buffer?
У встроенных функций/объектов сейчас размер строки ограничен только вместимостью дельфийской string.
AkulaBig писал(а):Кстати, DllCall даже в TCIMG есть.
Учитывая, что он сам написан на Autoit, в котором есть эта функция - почему бы не иметь.

Добавлено: Пн мар 31, 2025 22:40
Orion9
AkulaBig писал(а):Вы ведь и формат изменили. Параметра "handle") просто нет в спецификации. А это уже высший пилотаж.
Да бросьте, какой пилотаж ) Всё есть в спецификациях, просто вы пока не сталкивались с таким форматом вызова, но скоро будете щелкать его как семечки :)

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

HANDLE CreateFileW(
  [in]           LPCWSTR               lpFileName,
  [in]           DWORD                 dwDesiredAccess,
  [in]           DWORD                 dwShareMode,
  [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  [in]           DWORD                 dwCreationDisposition,
  [in]           DWORD                 dwFlagsAndAttributes,
  [in, optional] HANDLE                hTemplateFile
);
https://learn.microsoft.com/en-us/windo ... reatefilew
Вот здесь прямо говорится, что функция возвращает HANDLE, а следовательно опускать последний параметр в DllCall нельзя, иначе он будет принят как 4-байтный int, в то время как handle может оказаться 8-байтным в 64-битном приложении.

А в ReadFile можно опустить, т.к. она возвращает булево значенние равноценное int:

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

BOOL ReadFile(
  [in]                HANDLE       hFile,
  [out]               LPVOID       lpBuffer,
  [in]                DWORD        nNumberOfBytesToRead,
  [out, optional]     LPDWORD      lpNumberOfBytesRead,
  [in, out, optional] LPOVERLAPPED lpOverlapped
);
https://learn.microsoft.com/en-us/windo ... i-readfile
AkulaBig писал(а):А у вас тоже wstr. Надо будет попробовать.
Если работа идёт с ANSI-строками, то тип данных должен быть str, а чтение из буфера с указанием кодировки:

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

Local out = buf.GetStr(0, bytes, "ANSI")
Всё остальное, пожалуй, должно остаться без изменений, как мне представляется.

Добавлено: Вт апр 01, 2025 05:22
AkulaBig
Loopback писал(а):Это не у type, а ограничение функций плагинов.
Loopback писал(а):У встроенных функций/объектов сейчас размер строки ограничен только вместимостью дельфийской string.
Спасибо за разъяснения.
Loopback писал(а):не считаю проблему критической, особенно в свете наличия альтернатив
Я-бы даже сказал, что недавное обсуждение необходимости файловых функций сейчас кажется не очень нужными. Но для неискушенного пользователя будет удобным. Так что:
Loopback писал(а):Но в планах на доработку она есть
не отказывайтесь от планов. Удобная работа с плагином превыше всего. А возможность заменить кусок кода открытия и чтения файла одной строчкой очень удобна.
Orion9 писал(а): 4-байтный int
После ваших подробных объяснений пазл в голове сложился. Не очень удачные примеры из инета не позволили мне сразу разобраться с функционалом.
Orion9 писал(а):Если работа идёт с ANSI-строками
В том-то и дело, что изначально я писал про работу с юникодым наполнением. Поэтому и удивился вашему предложению сменить wstr на str. Собственно опять вопрос уже решен.

Добавлено: Вт апр 01, 2025 08:39
Orion9
AkulaBig писал(а):Собственно опять вопрос уже решен.
Т.е. переключение секций во всех файлах у вас заработало?

Похоже, решение с DllCall и ReadFile действительно рабочее. Переделал функцию CharDetect для кастомного поля плагина Autorun - шпарит только так :) Очень понравилась скорость:
[cut]

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

Pragma AutorunPluginFields "CharSet:::CharDetect"

Func CharDetect(FileName, FieldIndex, UnitIndex)
    Static GENERIC_READ = 0x80000000, _
           OPEN_EXISTING = 3, _    
           FILE_SHARE_READ = 1, _
           FILE_ATTRIBUTE_NORMAL = 128
           
    Static buf = Buffer(10240)           
    Static sLib = "uchardet" & (auX64 ? "" : "_x86") & ".dll", _
           hLib = DllCall("LoadLibrary", "wstr", COMMANDER_PATH & "\Ini\Tools\Libs" & sLib, "ptr")
           
    If hLib = 0 Then Return "Error LoadLibrary " & sLib
    
    Static hUC = DllCall(sLib & '\uchardet_new', 'ptr:cdecl')
    If hUC = 0 Then 
       Return "uchardet_new returned 0"
    Endif
    DllCall(sLib & '\uchardet_reset', 'ptr', hUC, 'cdecl')
        
    Local bytes = 0
    Local fs = FileGetSize(FileName)   
    buf.Zero()
    If fs > buf.Size Then fs = buf.Size    
    Local hFile = DllCall("CreateFileW", "wstr", FileName, _
                                         "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 Res = DllCall(sLib & '\uchardet_handle_data', 'ptr', hUC, 'ptr', buf.ptr, 'uint', fs, 'int:cdecl')
    If Res > 0 Then 
       Return "uchardet_handle_data returned " & Res
    Endif       
    DllCall(sLib & '\uchardet_data_end', 'ptr', hUC, 'cdecl')
    #DllCall('uchardet\uchardet_delete', 'ptr', hUC, 'cdecl')
    Res = DllCall(sLib & '\uchardet_get_charset', 'ptr', hUC, 'str:cdecl')
    # Ctrl - отобразить количество прочитанных байт
    Res = (IsPressed (0x11) ? "read: " & bytes : "") & ' ' & Res
    Return Res
EndFunc
[/cut]
Что касается детекта, нужно больше тестов, конечно, но показалось, что плохо иногда определяет некоторые кодировки. Сами библиотеки можно найти здесь:
https://github.com/telppa/ahk-chardet/tree/main/Lib

Добавлено: Вт апр 01, 2025 10:16
AkulaBig
Orion9 писал(а):Т.е. переключение секций во всех файлах у вас заработало?
Да.
Orion9 писал(а):Похоже, решение с DllCall и ReadFile действительно рабочее.
Точно рабочее.
Orion9 писал(а):нужно больше тестов
У меня есть специальный набор текстовых файлов для тестирования кодировок.
Orion9 писал(а):CharDetect
Это для детекта только русских кодировок или для любой?

Добавлено: Вт апр 01, 2025 11:02
Orion9
AkulaBig писал(а):Да.
Это хорошо. Поздравляю :)
AkulaBig писал(а):Это для детекта только русских кодировок или для любой?
Заявлено много кодировок. У них есть собственный набор файлов для тестов, но даже с ним можно наблюдать некоторые проблемы у нескольких файлов. Ещё самый простой способ - зайти в каталог LANGUAGE самого Тотала :)

Чуть дополнил код проверками, которые, как известно, лишними никогда не бывают:
[cut]

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

Pragma AutorunPluginFields "CharSet:::CharDetect"

Func CharDetect(FileName, FieldIndex, UnitIndex)
    Static GENERIC_READ = 0x80000000, _
           OPEN_EXISTING = 3, _    
           FILE_SHARE_READ = 1, _
           FILE_ATTRIBUTE_NORMAL = 128
           
    Static buf = Buffer(10240)           
    Static sLib = "uchardet" & (auX64 ? "" : "_x86") & ".dll", _
           hLib = DllCall("LoadLibrary", "wstr", COMMANDER_PATH & "\Ini\Tools\Libs" & sLib, "ptr")
           
    If StrPos(FileGetAttr(FileName), "D") Then Return "<Dir>"    
    If hLib = 0 Then Return "Error LoadLibrary " & sLib
    
    Static hUC = DllCall(sLib & '\uchardet_new', 'ptr:cdecl')
    If hUC = 0 Then 
       Return "uchardet_new returned 0"
    Endif
    DllCall(sLib & '\uchardet_reset', 'ptr', hUC, 'cdecl')
        
    Local bytes = 0
    Local fs = FileGetSize(FileName)   
    buf.Zero()
    If fs > buf.Size Then fs = buf.Size    
    Local hFile = DllCall("CreateFileW", "wstr",  FileName, _
                                         "dword", GENERIC_READ, _
                                         "dword", FILE_SHARE_READ, _
                                         "ptr",   0, _
                                         "dword", OPEN_EXISTING, _
                                         "dword", FILE_ATTRIBUTE_NORMAL, _
                                         "ptr",   0, _
                                         "handle")
    If hFile > 0 Then
       Local bRead = DllCall("ReadFile", "handle", hFile, _
                                         "ptr",    buf.ptr, _
                                         "dword",  fs, _
                                         "dword*", @bytes, _
                                         "ptr",    0, _
                                         "bool")
       DllCall("CloseHandle", "handle", hFile)
       If Not bRead Then Return "ReadFile failed"
       If bytes = 0 Then Return "ReadFile fetched 0 bytes"       
    Else
       Return "CreateFileW failed"
    Endif   
    Local Res = DllCall(sLib & '\uchardet_handle_data', 'ptr', hUC, 'ptr', buf.ptr, 'uint', bytes, 'int:cdecl')
    If Res > 0 Then 
       Return "uchardet_handle_data returned " & Res
    Endif       
    DllCall(sLib & '\uchardet_data_end', 'ptr', hUC, 'cdecl')
    #DllCall('uchardet\uchardet_delete', 'ptr', hUC, 'cdecl')
    Res = DllCall(sLib & '\uchardet_get_charset', 'ptr', hUC, 'str:cdecl')
    # Ctrl - отобразить количество прочитанных байт 
    Res = (IsPressed (0x11) ? "read: " & bytes : "") & ' ' & Res
    Return Res
EndFunc
[/cut]
Теперь, например, на симлинках показывает ошибку ReadFile ) Но вот заметил, что в ТС х64 наполнение кастомной колонки как-то не так происходит, как в 32-битном Тотале. Такое ощущение, что он сразу всю страницу пытается считать перед отображением. В 32-битном ТС работает глаже и приятнее.

Добавлено: Вт апр 01, 2025 12:18
AkulaBig
Orion9 писал(а):У них есть собственный набор файлов для тестов
Получается для всех кодировок. Хорошо. Когда дойдут руки надо посмотреть.

Добавлено: Вт апр 01, 2025 17:22
Orion9
AkulaBig
Если у вас ещё осталось желание подключить библиотеку bass, то теперь никакие внешние скрипты и плагины для этого не нужны, достаточно одного Autorun, не говоря уж о том, чтобы писать отдельный wdx-плагин для этих дел )

Добавлено: Вт апр 01, 2025 18:38
AkulaBig
И bass и MediaInfo. Я с того момента, как узнал про DllCall, думаю об этом. Специально пока не занимался многоязычностью секции HintsCustomField. Занимался пока настройками. Сейчас у меня в одном стиле сделаны переключение ассоциаций, цветовых схем, заголовка ТС, звуковых схем, значков дисков на панели, значков файловых панелей.
Сейчас вот еще поразбираюсь с заголовком пользовательских колонок и придет время библиотек.
Кстати, у вас нет идей?
Пользовательские колонки переключаются на английский и т.д. без перезагрузки. А вот их заголовок только после перезагрузки. Из-за чего мне и пришлось вставить перезагрузку ТС.
Есть вариант вообще убрать названия пользовательских колонок. Тогда по ПКМ их названия не показываются. Сделать кнопку для их переключения. Как вам такой вариант?
Или можно что-то сделать, чтобы названия переписывались через стандартный диалог? Только в этом случае можно поменять язык заголовка без перезагрузки.