Вопросы по использованию FFI

Столкнулись с проблемой в использовании макросов? Здесь можно получить помощь.
User avatar
John Doe
Бюрократ
Posts: 12192
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 31 times
Been thanked: 159 times

Вопросы по использованию FFI

Post by John Doe » Tue 21 May, 2013 20:20

FFI — одна из стандартных библиотек luajit.
Позволяет из Lua кода вызывать внешние C-функции и использовать структуры данных C.
В данной теме задаём вопросы по использованию FFI

Примеры готовых скриптов:

Предупреждение: viewtopic.php?p=147610#p147610

Shmuel
Posts: 5698
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 21 times
Been thanked: 200 times

Вопросы по использованию FFI

Post by Shmuel » Wed 13 Sep, 2017 17:36

Latest post of the previous page:

John Doe wrote:
Wed 13 Sep, 2017 17:28
В документации этот частный случай не рассматривается, сказано только в общем
На мой взгляд, сказано исчерпывающе ("This copies the whole string plus a terminating zero-byte").

User avatar
John Doe
Бюрократ
Posts: 12192
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 31 times
Been thanked: 159 times

Вопросы по использованию FFI

Post by John Doe » Wed 13 Sep, 2017 17:43

Меня ввело в заблужнение то, что непосредственно перед этим сказано: all remaining uninitialized elements are filled with zero bytes.
А так да, не спорю.

User avatar
John Doe
Бюрократ
Posts: 12192
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 31 times
Been thanked: 159 times

Вопросы по использованию FFI

Post by John Doe » Wed 29 Nov, 2017 09:30

Заготовка для документации:

0. Использование ffi (помимо усвоения документации библиотеки) в целом требует определённой квалификации, понимания работы вызываемых функций, осторожного обращения с памятью, и т.п.
Ниже следуют только дополнения, описывающие особенности использования ffi в фаре

1. Любое определение, сделанное посредством ffi.cdef, действует до конца сеанса.
Переопределить его невозможно: будет действовать определение, сделанное первым.
1.1. Определение, сделанное в одном скрипте будет действовать для всех, поэтому важно чтобы определения были корректными.
1.2. В случае изменения определения на несовместимое потребуется перезагрузить плагин (или фар).

2. В отличии от переопределения функций, попытка переопределения констант и именованных(?) структур ещё и вызывает ошибку времени выполнения, поэтому следует оборачивать вызов в pcall:

2.1. Следует учитывать, что "переопределение" неизбежно, это вполне штатная ситуация: например при перезагрузке макросов, или при использовании определений в независимых скриптах.

3. Для удобства luamacro при старте загружает файл winapi.lua, содержащий определения типов, часто используемых в winapi.

4. ???

Работа со строками, с юникодом, передача параметра по ссылке или значению (_OUT_/_IN_ в msdn) это то, что абсолютно необходимо понимать при работе с ffi.

Прочитав тему с начала можно найти ответы на множество вопросов.
Если же вы не готовы тратить своё время на то чтобы во всём разобраться, то и начинать не стоит.
Last edited by John Doe on Sat 12 May, 2018 16:50, edited 1 time in total.

Shmuel
Posts: 5698
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 21 times
Been thanked: 200 times

Вопросы по использованию FFI

Post by Shmuel » Wed 29 Nov, 2017 10:03

John Doe wrote:
Wed 29 Nov, 2017 09:30
1.2. В случае изменения определения на несовместимое потребуется перезагрузить плагин (или фар).
Или выполнить _G.RecreateLuaState = true.

Smitis
Posts: 1563
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 16 times
Been thanked: 32 times
Contact:

Вопросы по использованию FFI

Post by Smitis » Thu 30 Nov, 2017 08:25

John Doe wrote:
Wed 29 Nov, 2017 09:30
2. В отличии от переопределения функций, попытка переопределения констант и именованных(?) структур ещё и вызывает ошибку времени выполнения, поэтому следует оборачивать вызов в pcall:
В подавляющем большинстве случаев достаточно убрать имя структуры

Code: Select all

ffi.cdef [[
typedef struct { // _CONSOLE_SELECTION_INFO
  DWORD      dwFlags;
  COORD      dwSelectionAnchor;
  SMALL_RECT srSelection;
} CONSOLE_SELECTION_INFO, *PCONSOLE_SELECTION_INFO;
]]
Разум когда-нибудь победит.

User avatar
John Doe
Бюрократ
Posts: 12192
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 31 times
Been thanked: 159 times

Вопросы по использованию FFI

Post by John Doe » Thu 30 Nov, 2017 08:56

Smitis wrote:
Thu 30 Nov, 2017 08:25
В подавляющем большинстве случаев достаточно убрать имя структуры
Да, выше по теме было.
Но имя структуры используется в сообщениях об ошибках, поэтому я бы не стал его комментировать, safe_def предпочтительнее.

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

buniak_a_h
Posts: 705
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 67 times
Been thanked: 19 times
Contact:

Вопросы по использованию FFI

Post by buniak_a_h » Fri 11 May, 2018 01:35

Shmuel wrote:
Thu 19 Jun, 2014 15:34
Это не проблема, если сторонние скрипты будут вызывать require "winapi" перед своими ffi-определениями. Ошибка сразу и вылезет.
Написал микроскрипт:

Code: Select all

local winapi=require"winapi"
local ffi = require("ffi")
far.Show('qq')
При первом запуске (после перезагрузки скриптов) выдаётся следующая ошибка:

Code: Select all

╔════════════════════════════════════════════════════ LuaMacro ═════════════════════════════════════════════════════╗
║ ...ta\Roaming\Far Manager\Profile\Macros\modules\winapi.lua:319: attempt to redefine '_UNICODE_STRING' at line 52 ║
║ stack traceback:                                                                                                  ║
║    [C]: in function 'require'                                                                                     ║
║    \\?\D:\ttt\www.lua:1: in main chunk                                                                            ║
╟───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢
║                                                      { OK }                                                       ║
╚═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
Два традиционных вопроса: кто виноват и что делать?

buniak_a_h
Posts: 705
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 67 times
Been thanked: 19 times
Contact:

Вопросы по использованию FFI

Post by buniak_a_h » Fri 11 May, 2018 02:09

Господа! Помогите, кто может!
Есть маленький скрипт:
  1. local ffi = require("ffi")
  2. ffi.cdef[[
  3. BOOL CopyFileA(
  4.   LPCTSTR ExistingFileName,
  5.   LPCTSTR NewFileName,
  6.   BOOL    bFailIfExists
  7. );
  8.  
  9. int MessageBoxA(
  10.   HWND    hWnd,
  11.   LPCTSTR lpText,
  12.   LPCTSTR lpCaption,
  13.   UINT    uType
  14. );
  15.  
  16. ]]
  17. local inPath=win.GetCurrentDir() .. '\\Test.lua'
  18. local outPath=inPath .. '.out'
  19. if mf.fexist(outPath) then win.DeleteFile(outPath) end -- На всякий случай
  20. local inPathA, outPathA = -- Перевод в ASCII
  21.       win.WideCharToMultiByte(win.Utf8ToUtf16(inPath),win.GetACP()),
  22.       win.WideCharToMultiByte(win.Utf8ToUtf16(outPath),win.GetACP())
  23. if not far.Show(pcall(ffi.C.MessageBoxA, nil, inPathA, outPathA, 0)) then return end
  24. far.Show( pcall(ffi.C.CopyFileA, inPathA, outPathA, true) )
  25.  
Описывается интерфейс к двум функциям из WINAPI: MessageBoxA и CopyFileA. У обеих функций есть параметры типа LPCTSTR (строка в кодировке ASCII).
Вызов MessageBoxA проходит ожидаемо (выводятся обе строки - в заголовке и на форм) А вот при вызове CopyFileA почему-то возникает ошибка:
bad argument #1 to '?' (cannot convert 'string' to 'const unsigned short *')
При том, что параметры описаны абсолютно одинаково, аргумент-строка передается одна и та же...
Что я делаю не так? И как надо? Уже голову сломал...

2useven10
Posts: 3755
Joined: Mon 07 Sep, 2009 10:40
Has thanked: 4 times
Been thanked: 148 times

Вопросы по использованию FFI

Post by 2useven10 » Fri 11 May, 2018 06:41

Если вы используете неюникодные ф-и (почему?), то описывайте строковые аргументы как LPCSTR.
LPCTSTR - условный тип, который в зависимости от дефайнов считается либо LPCSTR, либо в LPCWSTR.
Видимо дефайны у вас и у ffi разные.

2useven10
Posts: 3755
Joined: Mon 07 Sep, 2009 10:40
Has thanked: 4 times
Been thanked: 148 times

Вопросы по использованию FFI

Post by 2useven10 » Fri 11 May, 2018 06:50

Лучше пользоваться юникодными вариантами ф-й -- MessgeBoxW, CopyFileW и соответственно LPCWSTR.
Лишнее преобразование из юникода при этом надо выкинуть.
Юникодные строки должны закачиваться юникодным нулём. Lua сам такого не делает, поэтому
приходится в конце строки явно добавлять свой ноль -- "filename\0".

Shmuel
Posts: 5698
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 21 times
Been thanked: 200 times

Вопросы по использованию FFI

Post by Shmuel » Fri 11 May, 2018 06:54

buniak_a_h wrote:
Fri 11 May, 2018 01:35
Два традиционных вопроса: кто виноват и что делать?
Аналогичный вопрос уже задавался в данной ветке.
Ответ: pcall(require,"winapi")
buniak_a_h wrote:
Fri 11 May, 2018 02:09
Что я делаю не так? И как надо?
Надо в определениях заменить LPCTSTR на const char*
И как верно заметили выше, желательно работать с юникодными версиями функций Windows API. Зачем возвращаться к давно преодолённым ограничениям ASCII.
Last edited by Shmuel on Fri 11 May, 2018 06:58, edited 1 time in total.

2useven10
Posts: 3755
Joined: Mon 07 Sep, 2009 10:40
Has thanked: 4 times
Been thanked: 148 times

Вопросы по использованию FFI

Post by 2useven10 » Fri 11 May, 2018 08:04

buniak_a_h wrote:
Fri 11 May, 2018 02:09
Есть маленький скрипт:
у меня работает. (немного поправил)
Last edited by 2useven10 on Fri 11 May, 2018 12:19, edited 2 times in total.

buniak_a_h
Posts: 705
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 67 times
Been thanked: 19 times
Contact:

Вопросы по использованию FFI

Post by buniak_a_h » Fri 11 May, 2018 08:37

Shmuel wrote:
Fri 11 May, 2018 06:54
buniak_a_h wrote:
Fri 11 May, 2018 01:35
Два традиционных вопроса: кто виноват и что делать?
Аналогичный вопрос уже задавался в данной ветке.
Ответ: pcall(require,"winapi")
Это если б мне надо было, чтоб заработало. Но Вы пишете, что require"winapi" нужно для получения диагностики. «Ошибка сразу и вылезла». Вот она и вылезла, при том, что своих определений я не давал...
Впрочем, вопрос академический. У кого-то ошибка... Я просто убрал require"winapi".
buniak_a_h wrote:
Fri 11 May, 2018 02:09
Что я делаю не так? И как надо?
Надо в определениях заменить LPCTSTR на const char*
Это - первое, что я попробовал :-( . Это - не помогает...
Shmuel wrote:
Fri 11 May, 2018 06:54
Зачем возвращаться к давно преодолённым ограничениям ASCII.
Пример с MessageBoxA взят из FFI Library. Просто я ничего не понимаю в кодировках, сделал как попугай, по образу и подобию примера. А там почему-то "A"-вариант.

buniak_a_h
Posts: 705
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 67 times
Been thanked: 19 times
Contact:

Вопросы по использованию FFI

Post by buniak_a_h » Fri 11 May, 2018 08:45

2useven10 wrote:
Fri 11 May, 2018 08:04
у меня работает.
У меня - нет. Т.е. ошибки не выдаёт, но и файл не копирует. Просто уничтожает. Может, дело в том, что \0 добавлен в inPath, а outPath получен дописыванием к InPath ещё чего-то (за финальным нулём!)?

buniak_a_h
Posts: 705
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 67 times
Been thanked: 19 times
Contact:

Вопросы по использованию FFI

Post by buniak_a_h » Fri 11 May, 2018 08:56

2useven10 wrote:
Fri 11 May, 2018 08:04
у меня работает.
Добавочка.
Если из inPath сначала убрать .. '\0', а затем отдельной строкой написать
inPath = inPath .. '\0'
то вылет следующий:

Code: Select all

╔══════════════════════════ Exception occurred ══════════════════════════╗
║ Exception:   0xC0000005 - EXCEPTION_ACCESS_VIOLATION                   ║
║ Details:     Memory at FFFFFFFFFFFFFFFE could not be read              ║
║ Address:     0x7FFCE9EE5F0F - KERNELBASE.dll!BasepCopyFileCallback     ║
║ Source:                                                                ║
║ Function:    OpenW                                                     ║
║ Module:      :\Program Files\Far Manager\Plugins\LuaMacro\LuaMacro.dll ║
╟────────────────────────────────────────────────────────────────────────╢
║          { Unload plugin } [ Stack ] [ Minidump ] [ Ignore ]           ║
╚════════════════════════════════════════════════════════════════════════╝

2useven10
Posts: 3755
Joined: Mon 07 Sep, 2009 10:40
Has thanked: 4 times
Been thanked: 148 times

Вопросы по использованию FFI

Post by 2useven10 » Fri 11 May, 2018 08:58

да, если дело доходит до копирования у меня так же падает...

buniak_a_h
Posts: 705
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 67 times
Been thanked: 19 times
Contact:

Вопросы по использованию FFI

Post by buniak_a_h » Fri 11 May, 2018 09:04

2useven10 wrote:
Fri 11 May, 2018 08:58
да, если дело доходит до копирования у меня так же падает...

Если б Вы ещё написали, что такое ffi.cast (ну или хотя бы где про него прочитать)... А то повторяю как попугай... :-(

Post Reply

Return to “Нужна помощь”