FFIError - модуль обработки ошибок функций

Здесь обсуждается всё, что касается макросов.
IgorZ
Posts: 790
Joined: Wed 13 Jul, 2011 03:32
Has thanked: 15 times
Been thanked: 93 times

FFIError - модуль обработки ошибок функций

Post by IgorZ »

Очень простенький модуль. Содержит единственную функцию, которая проверяет результат выполнения последней функции, вызванной с помощью FFI, получает расшифровку кода завершения и возвращает то и другое. Разумеется, проверять результат следует в том случае, если результат работы функции отличен от нормального (возратила признак ошибки, не возвратила корректный результат и т. п.), иначе результат, возвращаемый модулем, не имеет практического смысла.
Можно также использовать с аналогичной целью far.Message с параметром "e" (far. Message(<базовый текст сообщения>, <заголовок>, <кнопки>, "e")). Отличия данного модуля:
  • не выводит на экран ничего, что позволяет реагировать более гибко;
  • кроме текста ошибки возвращает его код, который удобен для программного анализа.
Пример использования:
  1. local ffi = require"ffi"
  2.  
  3. local ffierror = require"ffierror"
  4.  
  5. local C = ffi.C
  6.  
  7. ffi.cdef[[
  8.  
  9. HWND GetActiveWindow(VOID);
  10.  
  11. ]]
  12.  
  13. local wh = C.GetActiveWindow()
  14.  
  15. local code,text = ffierror()
  16.  
  17. if wh then
  18.  
  19.   far.Show(code,text, "wh="..tostring(wh))
  20.  
  21. else
  22.  
  23.   far.Show("Error #"..code..". "..text)
  24.  
  25. end
В качестве бонуса выкладываю первоначальную (пока у меня были сложности с дрессировкой FormatMessage) версию. Она содержит все коды ошибок внутри, текст ошибок только английский, зато возвращает три параметра вместо двух: код завершения, наименование кода и его расшифровку. В принципе, можно использовать как справочник.

Требуемая версия Far 3.0.3001+.

Attachments
ffierror.1.0.3.7z
(1.32 KiB) Downloaded 51 times
ffierror.0.1.7z
(72.38 KiB) Downloaded 65 times
Last edited by IgorZ on Wed 29 Nov, 2017 10:39, edited 4 times in total.
IgorZ
Posts: 790
Joined: Wed 13 Jul, 2011 03:32
Has thanked: 15 times
Been thanked: 93 times

FFIError - модуль обработки ошибок функций

Post by IgorZ »

Наваяно в связи с непонятным поведением некоторых функций и необходимостью проверять результаты их исполнения.
User avatar
John Doe
Бюрократ
Posts: 13807
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 73 times
Been thanked: 426 times
Contact:

FFIError - модуль обработки ошибок функций

Post by John Doe »

IgorZ wrote: Fri 28 Jul, 2017 10:52 проверяет результат выполнения последней функции, вызванной с помощью FFI
Не совсем корректная формулировка. Можно показаться будто (1) GetLastError только от ffi зависит и (2) любой вызов ffi должен установить какой-то код ошибки.

Поэтому хочу уточнить, что ожидать код ошибки стоит только в тех случаях, когда об этом явно сказано в MSDN.
Причём не всегда в случае отсутствия ошибки код будет 0, иногда успех надо определять по возвращаемому значению функции, и только в случае ошибки обращаться к GetLastError (в противном случае мы прочитаем код какой-то ошибки, оставшийся от какого-то прошлого вызова совершенно другой функции, не нами даже вызванной).
Собственно, я только что пересказал описание из MSDN

Также хочу обратить внимание, что подобная функциональность уже есть, в функции Message (FMSG_ERRORTYPE)
User avatar
John Doe
Бюрократ
Posts: 13807
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 73 times
Been thanked: 426 times
Contact:

FFIError - модуль обработки ошибок функций

Post by John Doe »

В lua в простейшем случае это так: far.Message("",nil,nil,"ew")
Для примера кусок кода:
IgorZ
Posts: 790
Joined: Wed 13 Jul, 2011 03:32
Has thanked: 15 times
Been thanked: 93 times

FFIError - модуль обработки ошибок функций

Post by IgorZ »

*грустно* Про такую функциональность far.Message я и забыл... Куча времени потрачена практически впустую... Эх!
Ладно, спасибо за подсказку. Описание тоже подправлю, для порядка.
Last edited by IgorZ on Mon 31 Jul, 2017 06:15, edited 1 time in total.
IgorZ
Posts: 790
Joined: Wed 13 Jul, 2011 03:32
Has thanked: 15 times
Been thanked: 93 times

FFIError - модуль обработки ошибок функций

Post by IgorZ »

Подумав, решил, что раз уж выложил, то надо поправить в соответствии с. Так что
Версия 1.0.1 - Добавлено определение malloc() и free(). Дополнено Info.
User avatar
John Doe
Бюрократ
Posts: 13807
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 73 times
Been thanked: 426 times
Contact:

FFIError - модуль обработки ошибок функций

Post by John Doe »

Возникло несколько замечаний по оптимальному использованию ffi.
  1. Функция C.wcslen в коде не определена, работает только благодаря тому, её определение по случайности уже произошло "где-то там".
    Впрочем, она и не нужна, ведь длину возвращает FormatMessageW (кстати в условии у вас ошибка, надо сравнивать с 0)
  2. Code: Select all

    local err = ffi.C.GetLastError()
    Насчёт кэширования функций есть такие рекомендации: http://luajit.org/ext_ffi_tutorial.html#cache
  3. Code: Select all

    local buf = ffi.gc(ffi.C.malloc(2048), ffi.C.free)
    
    Необходимости в malloc/free нет, ведь есть ffi.new.
    И если уж использовать malloc, то незачем дожидаться сборщика мусора, ведь в данном случае мы чётко знаем, когда буфер нам уже не нужен, и можем освободить его сразу.
  4. Также не уверен, что размер буфера выбран правильно (2048).
    Требуемый размер заранее неизвестен, зато есть флаг FORMAT_MESSAGE_ALLOCATE_BUFFER, почему его не использовать?
    (Впрочем, не знаю деталей реализации FORMAT_MESSAGE_ALLOCATE_BUFFER, возможно в этом случае тупо выделяется 64 кБ)
Last edited by John Doe on Tue 07 Nov, 2017 10:44, edited 2 times in total.
IgorZ
Posts: 790
Joined: Wed 13 Jul, 2011 03:32
Has thanked: 15 times
Been thanked: 93 times

FFIError - модуль обработки ошибок функций

Post by IgorZ »

John Doe wrote: Tue 07 Nov, 2017 09:11
На самом деле, всё просто. Я раньше самостоятельно не писал ничего с использованием FFI, пользуясь чужими образцами и подсказками, так что этот модуль - по большей части тренировочный. Отсюда и все возникшие вопросы (скажем, со сборщиком мусора мне было просто интересно сделать именно так). За замечания спасибо, буду посвободней - поразбираюсь.
IgorZ
Posts: 790
Joined: Wed 13 Jul, 2011 03:32
Has thanked: 15 times
Been thanked: 93 times

FFIError - модуль обработки ошибок функций

Post by IgorZ »

Версия 1.0.2 - Исправления, оптимизация.
User avatar
John Doe
Бюрократ
Posts: 13807
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 73 times
Been thanked: 426 times
Contact:

FFIError - модуль обработки ошибок функций

Post by John Doe »

У вас:

Code: Select all

LPTSTR *lpBuffer
В документации:

Code: Select all

LPTSTR  lpBuffer
Грозит тем, что где-то в другом скрипте будет другое определение, и один из двух отвалится (тот что загружен вторым).

И ещё: в случае ошибки как я понимаю буфер не выделяется, не знаю насколько безопасно вызывать LocalFree.
Впрочем скорее всего безопасно:
Last edited by John Doe on Tue 28 Nov, 2017 14:11, edited 1 time in total.
IgorZ
Posts: 790
Joined: Wed 13 Jul, 2011 03:32
Has thanked: 15 times
Been thanked: 93 times

FFIError - модуль обработки ошибок функций

Post by IgorZ »

John Doe wrote: Tue 28 Nov, 2017 14:06 У вас:

Code: Select all

LPTSTR *lpBuffer
В документации:

Code: Select all

LPTSTR  lpBuffer
Грозит тем, что где-то в другом скрипте будет другое определение, и один из двух отвалится (тот что загружен вторым).
К сожалению, пока я этого не сделал, FormatMessageW с FORMAT_MESSAGE_ALLOCATE_BUFFER отказывался работать. Дело в том, что без FORMAT_MESSAGE_ALLOCATE_BUFFER lpBuffer это адрес буфера для выходной строки, а с FORMAT_MESSAGE_ALLOCATE_BUFFER это адрес ячейки, в которую пишется адрес буфера для выходной строки. И если не отразить это в описании, то при запуске получаем

Возможно, это и можно как-то обойти, но в таком случае моих знаний FFI для этого не хватает.
P. S. Описание в документации относится к варианту без флага FORMAT_MESSAGE_ALLOCATE_BUFFER.
P. P. S. Ох, и намучался же я, прежде чем понял, что недостаточно перегрузить макросы и выгрузить модуль ffierror для того, чтобы изменения в ffi.cdef подействовали, и надо перезапустить Far. Это я просто так жалуюсь...
User avatar
John Doe
Бюрократ
Posts: 13807
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 73 times
Been thanked: 426 times
Contact:

FFIError - модуль обработки ошибок функций

Post by John Doe »

IgorZ wrote: Wed 29 Nov, 2017 05:18Возможно, это и можно как-то обойти, но в таком случае моих знаний FFI для этого не хватает.
you must cast the pointer to an LPTSTR (for example, (LPTSTR)&lpBuffer).
Видимо так:

Code: Select all

  local buf_ptr = ffi.new("LPTSTR[1]")
  local len = C.FormatMessageW(dwFlags,nil,errCode,0,ffi.cast("LPTSTR",buf_ptr),0,nil)
  if len~=0 then
    local msg = win.Utf16ToUtf8(ffi.string(buf_ptr[0],len*wsize))
  -- ...    
IgorZ wrote: Wed 29 Nov, 2017 05:18P. S. Описание в документации относится к варианту без флага FORMAT_MESSAGE_ALLOCATE_BUFFER.
Декларацию всё равно изменять нельзя. А приводить типы - сколько угодно.
IgorZ wrote: Wed 29 Nov, 2017 05:18Ох, и намучался же я, прежде чем понял, что недостаточно перегрузить макросы и выгрузить модуль ffierror для того, чтобы изменения в ffi.cdef подействовали, и надо перезапустить Far.
Справедливое замечание. Стоит отразить в документации особенности работы с ffi (safe_cdef и всё такое).
IgorZ
Posts: 790
Joined: Wed 13 Jul, 2011 03:32
Has thanked: 15 times
Been thanked: 93 times

FFIError - модуль обработки ошибок функций

Post by IgorZ »

Версия 1.0.3 - Приведение к совсем каноничному виду.
John Doe wrote: Wed 29 Nov, 2017 08:35
IgorZ wrote: Wed 29 Nov, 2017 05:18Возможно, это и можно как-то обойти, но в таком случае моих знаний FFI для этого не хватает.
Видимо так:

Code: Select all

  local buf_ptr = ffi.new("LPTSTR[1]")
  local len = C.FormatMessageW(dwFlags,nil,errCode,0,ffi.cast("LPTSTR",buf_ptr),0,nil)
  if len~=0 then
    local msg = win.Utf16ToUtf8(ffi.string(buf_ptr[0],len*wsize))
  -- ...    
Ага, работает, спасибо. Блин, написано же для ffi.cast:
This functions is mainly useful to override the pointer compatibility checks or to convert pointers to addresses or vice versa.
Но нет, несколько раз перечитывал документацию, а это в упор не увидел!
John Doe wrote: Wed 29 Nov, 2017 08:35
IgorZ wrote: Wed 29 Nov, 2017 05:18P. S. Описание в документации относится к варианту без флага FORMAT_MESSAGE_ALLOCATE_BUFFER.
Декларацию всё равно изменять нельзя. А приводить типы - сколько угодно.
Изменять можно. Просто помнить, что бывают разные неожиданные последствия. И не жаловаться потом.
Last edited by IgorZ on Wed 29 Nov, 2017 10:58, edited 1 time in total.
User avatar
John Doe
Бюрократ
Posts: 13807
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 73 times
Been thanked: 426 times
Contact:

FFIError - модуль обработки ошибок функций

Post by John Doe »

IgorZ wrote: Wed 29 Nov, 2017 10:52Изменять можно.
Например?
IgorZ
Posts: 790
Joined: Wed 13 Jul, 2011 03:32
Has thanked: 15 times
Been thanked: 93 times

FFIError - модуль обработки ошибок функций

Post by IgorZ »

John Doe wrote: Wed 29 Nov, 2017 10:54Например?
Это, скорее, философское замечание. Также как можно ремонтировать электропроводку, не отключая предохранители, заплывать за буйки и кормить с рук хищников в зоопарке.
P. S. В любом из перечисленных случаев неприятных последствий может и не быть. Но если случились - винить некого, кроме себя.
Last edited by IgorZ on Wed 29 Nov, 2017 11:02, edited 2 times in total.
Post Reply

Return to “Обсуждение макросов”