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

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

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

Post by John Doe »

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

Заготовка для документации: viewtopic.php?f=57&t=8418&p=147610#p147610

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

Предупреждение: viewtopic.php?p=147610#p147610
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

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

Post by Smitis »

Latest post of the previous page:

John Doe wrote: Wed 29 Nov, 2017 09:302. В отличии от переопределения функций, попытка переопределения констант и именованных(?) структур ещё и вызывает ошибку времени выполнения, поэтому следует оборачивать вызов в 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: 13807
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 73 times
Been thanked: 426 times
Contact:

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

Post by John Doe »

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

На самом деле любая попытка переопределения это действительно ошибка, и даже странно что её нет с безымянными структурами и функциями.
В идеале было бы, если бы повторное определение проверялось на соответствие с имеющимся, и ошибка выдавалась бы в случае несовместимых определений.
Но нет, повторные просто проглатываются, не выдавая ошибок, но и не переопределяя.
User avatar
buniak_a_h
Posts: 4236
Joined: Sat 20 Apr, 2013 00:17
Location: Санкт-Петробад
Has thanked: 222 times
Been thanked: 643 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 }                                                       ║
╚═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
Два традиционных вопроса: кто виноват и что делать?
User avatar
buniak_a_h
Posts: 4236
Joined: Sat 20 Apr, 2013 00:17
Location: Санкт-Петробад
Has thanked: 222 times
Been thanked: 643 times
Contact:

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

Post by buniak_a_h »

Господа! Помогите, кто может!
Есть маленький скрипт:
  1. local ffi = require("ffi")
  2.  
  3. ffi.cdef[[
  4.  
  5. BOOL CopyFileA(
  6.  
  7.   LPCTSTR ExistingFileName,
  8.  
  9.   LPCTSTR NewFileName,
  10.  
  11.   BOOL    bFailIfExists
  12.  
  13. );
  14.  
  15.  
  16.  
  17. int MessageBoxA(
  18.  
  19.   HWND    hWnd,
  20.  
  21.   LPCTSTR lpText,
  22.  
  23.   LPCTSTR lpCaption,
  24.  
  25.   UINT    uType
  26.  
  27. );
  28.  
  29.  
  30.  
  31. ]]
  32.  
  33. local inPath=win.GetCurrentDir() .. '\\Test.lua'
  34.  
  35. local outPath=inPath .. '.out'
  36.  
  37. if mf.fexist(outPath) then win.DeleteFile(outPath) end -- На всякий случай
  38.  
  39. local inPathA, outPathA = -- Перевод в ASCII
  40.  
  41.       win.WideCharToMultiByte(win.Utf8ToUtf16(inPath),win.GetACP()),
  42.  
  43.       win.WideCharToMultiByte(win.Utf8ToUtf16(outPath),win.GetACP())
  44.  
  45. if not far.Show(pcall(ffi.C.MessageBoxA, nil, inPathA, outPathA, 0)) then return end
  46.  
  47. far.Show( pcall(ffi.C.CopyFileA, inPathA, outPathA, true) )
  48.  
  49.  
Описывается интерфейс к двум функциям из WINAPI: MessageBoxA и CopyFileA. У обеих функций есть параметры типа LPCTSTR (строка в кодировке ASCII).
Вызов MessageBoxA проходит ожидаемо (выводятся обе строки - в заголовке и на форм) А вот при вызове CopyFileA почему-то возникает ошибка:
bad argument #1 to '?' (cannot convert 'string' to 'const unsigned short *')
При том, что параметры описаны абсолютно одинаково, аргумент-строка передается одна и та же...
Что я делаю не так? И как надо? Уже голову сломал...
2useven10
Posts: 5209
Joined: Mon 07 Sep, 2009 10:40
Has thanked: 18 times
Been thanked: 310 times

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

Post by 2useven10 »

Если вы используете неюникодные ф-и (почему?), то описывайте строковые аргументы как LPCSTR.
LPCTSTR - условный тип, который в зависимости от дефайнов считается либо LPCSTR, либо в LPCWSTR.
Видимо дефайны у вас и у ffi разные.
2useven10
Posts: 5209
Joined: Mon 07 Sep, 2009 10:40
Has thanked: 18 times
Been thanked: 310 times

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

Post by 2useven10 »

Лучше пользоваться юникодными вариантами ф-й -- MessgeBoxW, CopyFileW и соответственно LPCWSTR.
Лишнее преобразование из юникода при этом надо выкинуть.
Юникодные строки должны закачиваться юникодным нулём. Lua сам такого не делает, поэтому
приходится в конце строки явно добавлять свой ноль -- "filename\0".
Shmuel
Posts: 6820
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 528 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: 5209
Joined: Mon 07 Sep, 2009 10:40
Has thanked: 18 times
Been thanked: 310 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.
User avatar
buniak_a_h
Posts: 4236
Joined: Sat 20 Apr, 2013 00:17
Location: Санкт-Петробад
Has thanked: 222 times
Been thanked: 643 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"-вариант.
User avatar
buniak_a_h
Posts: 4236
Joined: Sat 20 Apr, 2013 00:17
Location: Санкт-Петробад
Has thanked: 222 times
Been thanked: 643 times
Contact:

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

Post by buniak_a_h »

2useven10 wrote: Fri 11 May, 2018 08:04 у меня работает.
У меня - нет. Т.е. ошибки не выдаёт, но и файл не копирует. Просто уничтожает. Может, дело в том, что \0 добавлен в inPath, а outPath получен дописыванием к InPath ещё чего-то (за финальным нулём!)?
User avatar
buniak_a_h
Posts: 4236
Joined: Sat 20 Apr, 2013 00:17
Location: Санкт-Петробад
Has thanked: 222 times
Been thanked: 643 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: 5209
Joined: Mon 07 Sep, 2009 10:40
Has thanked: 18 times
Been thanked: 310 times

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

Post by 2useven10 »

да, если дело доходит до копирования у меня так же падает...
User avatar
buniak_a_h
Posts: 4236
Joined: Sat 20 Apr, 2013 00:17
Location: Санкт-Петробад
Has thanked: 222 times
Been thanked: 643 times
Contact:

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

Post by buniak_a_h »

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

Если б Вы ещё написали, что такое ffi.cast (ну или хотя бы где про него прочитать)... А то повторяю как попугай... :-(
Shmuel
Posts: 6820
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 528 times

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

Post by Shmuel »

buniak_a_h wrote: Fri 11 May, 2018 09:04 Если б Вы ещё написали, что такое ffi.cast (ну или хотя бы где про него прочитать
Ну есть же сайт luajit, там вся документация по FFI.
User avatar
buniak_a_h
Posts: 4236
Joined: Sat 20 Apr, 2013 00:17
Location: Санкт-Петробад
Has thanked: 222 times
Been thanked: 643 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: 6820
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 528 times

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

Post by Shmuel »

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

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