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

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

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

Post by John Doe »

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

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

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

Smitis
Posts: 1605
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 18 times
Been thanked: 39 times

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

Post by Smitis »

Latest post of the previous page:

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: 12603
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 35 times
Been thanked: 260 times

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

Post by John Doe »

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

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

buniak_a_h
Posts: 1401
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 98 times
Been thanked: 108 times
Contact:

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

Post by buniak_a_h »

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: 1401
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 98 times
Been thanked: 108 times
Contact:

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

Post by buniak_a_h »

Господа! Помогите, кто может!
Есть маленький скрипт:
  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: 4002
Joined: Mon 07 Sep, 2009 10:40
Has thanked: 5 times
Been thanked: 179 times

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

Post by 2useven10 »

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

2useven10
Posts: 4002
Joined: Mon 07 Sep, 2009 10:40
Has thanked: 5 times
Been thanked: 179 times

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

Post by 2useven10 »

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

Shmuel
Posts: 5972
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 25 times
Been thanked: 257 times

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

Post by Shmuel »

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: 4002
Joined: Mon 07 Sep, 2009 10:40
Has thanked: 5 times
Been thanked: 179 times

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

Post by 2useven10 »

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: 1401
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 98 times
Been thanked: 108 times
Contact:

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

Post by buniak_a_h »

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: 1401
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 98 times
Been thanked: 108 times
Contact:

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

Post by buniak_a_h »

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

buniak_a_h
Posts: 1401
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 98 times
Been thanked: 108 times
Contact:

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

Post by buniak_a_h »

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: 4002
Joined: Mon 07 Sep, 2009 10:40
Has thanked: 5 times
Been thanked: 179 times

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

Post by 2useven10 »

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

buniak_a_h
Posts: 1401
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 98 times
Been thanked: 108 times
Contact:

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

Post by buniak_a_h »

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

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

Shmuel
Posts: 5972
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 25 times
Been thanked: 257 times

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

Post by Shmuel »

buniak_a_h wrote:
Fri 11 May, 2018 09:04
Если б Вы ещё написали, что такое ffi.cast (ну или хотя бы где про него прочитать
Ну есть же сайт luajit, там вся документация по FFI.

buniak_a_h
Posts: 1401
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 98 times
Been thanked: 108 times
Contact:

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

Post by buniak_a_h »

Shmuel wrote:
Fri 11 May, 2018 09:06
Ну есть же сайт luajit, там вся документация по FFI.
Ну вот как раз с этого сайта я и брал не-юникодный пример MessageBoxA. И, кстати, в этом примере ffi.cast НЕ БЫЛО! А работало прекрасно. Хотя именно в том примере я был уверен, что требуется преобразование типа - ведь раз интерпретатор работает с юникодными строками, то по логике и адрес должен указывать именно на юникодную строку... Но... Логика тут бессильна, просто надо запомнить... :cry:
Я догадался, что ffi.cast - это нечто типа приведения типа. Но как узнать, когда оно нужно, а когда - нет, этот сайт не разъясняет.

Shmuel
Posts: 5972
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 25 times
Been thanked: 257 times

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

Post by Shmuel »

buniak_a_h wrote:
Fri 11 May, 2018 09:26
Я догадался, что ffi.cast - это нечто типа приведения типа. Но как узнать, когда оно нужно, а когда - нет, этот сайт не разъясняет.
Документация по FFI (как и многие другие мануалы для программистов) предполагает определённый уровень подготовки читающего.
Как на мой взгляд, документация по FFI на сайте LuaJIT очень качественная.

Post Reply

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