JNA: указанная процедура не найдена - PullRequest
3 голосов
/ 01 марта 2011

Я пытался узнать, как работает JNA, поэтому я решил использовать API spotify (libspotify 0.0.7).Мне удалось правильно загрузить мою dll, но тогда похоже, что мой код не находит ни один из методов, определенных в API.

Вот мой код:

Мой основной файл:

public class Test{
    private static final int SPOTIFY_API_VERSION = 7;
private static final char[] APP_KEY = { /* MY APP KEY HERE */ };

    static{
        System.loadLibrary("libspotify");
    }

    public static void main(String[] args){
    JLibspotify libs = JLibspotify.INSTANCE;

    sp_session mySession = new sp_session();
    sp_session_config cfg = new sp_session_config();
    cfg.api_version = SPOTIFY_API_VERSION;
    cfg.cache_location = "tmp";
    cfg.settings_location = "tmp";
    cfg.application_key = APP_KEY;
    cfg.application_key_size = APP_KEY.length;
    cfg.user_agent = "spshell";
    cfg.callbacks = null;

    libs.sp_session_create(cfg, mySession);
}
}

Интерфейс моей библиотеки:

public interface JLibspotify extends Library {  
    JLibspotify INSTANCE = (JLibspotify)Native.loadLibrary("libspotify", JLibspotify.class);

    // Methods definitions
    sp_error sp_session_create(sp_session_config config, sp_session sess);
}

Мой объект sp_session (непрозрачная структура C)

public class sp_session extends PointerType{
    public sp_session(Pointer address) {
        super(address);
    }
    public sp_session() {
        super();
    }
}

Мой объект sp_session_config

public class sp_session_config extends Structure{
    public int api_version; // The version of the Spotify API your application is compiled with.
    public String cache_location;
    public String settings_location;
    public char[] application_key}; // Your application key.
    public int application_key_size; // The size of the application key in bytes
    public String user_agent;
    public sp_session_callbacks callbacks; // Delivery callbacks for session events. NULL if not interested in any callbacks
    public Pointer userdata; // User supplied data for your application
    public boolean compress_playlists;
    public boolean dont_save_metadata_for_playlists;
    public boolean initially_unload_playlists;
}

Мой sp_error enum

public enum sp_error {
    SP_ERROR_OK, 
    SP_ERROR_BAD_API_VERSION, 
    SP_ERROR_API_INITIALIZATION_FAILED, 
    SP_ERROR_TRACK_NOT_PLAYABLE, 
    SP_ERROR_RESOURCE_NOT_LOADED, 
    SP_ERROR_BAD_APPLICATION_KEY, 
    SP_ERROR_BAD_USERNAME_OR_PASSWORD, 
    SP_ERROR_USER_BANNED, 
    SP_ERROR_UNABLE_TO_CONTACT_SERVER, 
    SP_ERROR_CLIENT_TOO_OLD, 
    SP_ERROR_OTHER_PERMANENT, 
    SP_ERROR_BAD_USER_AGENT, 
    SP_ERROR_MISSING_CALLBACK, 
    SP_ERROR_INVALID_INDATA, 
    SP_ERROR_INDEX_OUT_OF_RANGE, 
    SP_ERROR_USER_NEEDS_PREMIUM, 
    SP_ERROR_OTHER_TRANSIENT, 
    SP_ERROR_IS_LOADING, 
    SP_ERROR_NO_STREAM_AVAILABLE, 
    SP_ERROR_PERMISSION_DENIED, 
    SP_ERROR_INBOX_IS_FULL, 
    SP_ERROR_NO_CACHE, 
    SP_ERROR_NO_SUCH_USER
}

Моя трассировка стека исключений

Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'sp_session_create': The specified procedure could not be found.

at com.sun.jna.Function.<init>(Function.java:129)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:250)
at com.sun.jna.Library$Handler.invoke(Library.java:191)
at $Proxy0.sp_session_create(Unknown Source)
at com.nbarraille.jspotify.main.Test.main(Test.java:49)

Объявление C ++ метода, который я пытаюсь запустить

/**
 * Initialize a session. The session returned will be initialized, but you will need
 * to log in before you can perform any other operation
 *
 * Here is a snippet from \c spshell.c:
 * @dontinclude spshell.c
 * @skip config.api_version
 * @until }
 *
 * @param[in]   config    The configuration to use for the session
 * @param[out]  sess      If successful, a new session - otherwise NULL
 *
 * @return                One of the following errors, from ::sp_error
 *                        SP_ERROR_OK
 *                        SP_ERROR_BAD_API_VERSION
 *                        SP_ERROR_BAD_USER_AGENT
 *                        SP_ERROR_BAD_APPLICATION_KEY
 *                        SP_ERROR_API_INITIALIZATION_FAILED
 */
SP_LIBEXPORT(sp_error) sp_session_create(const sp_session_config *config, sp_session **sess);

Ответы [ 3 ]

2 голосов
/ 02 марта 2011

I наконец-то нашел решение, открыв libspotify.dll с помощью Dependency Walker: компилятор добавил некоторую дополнительную информацию к имени метода (префикс подчеркивания и суффикс @ 4 или @ 8).

Мне пришлось:

  • Создать реализацию FunctionMapper, которая переименовала все мои методы в соответствии с реальными именами (доступно в Dependency Walker)
  • Создание экземпляра моей библиотеки с помощью экземпляраэтого картографа в карте параметров.
1 голос
/ 01 марта 2011
    By the way, I don't have access to the definition of the sp_artist structure in C, I just reconstructed it based on the methods offered by the API, could it be the problem?

Если у вас нет доступа к нему, то нет и JNA.Если это непрозрачный тип, ищите функции для его создания, изменения и удаления.

Кроме того, вы получили ошибку в предыдущем выражении, пятистрочном определении переменной Java "artist"?

0 голосов
/ 25 января 2018

@ technomage комментарий очень полезен. Вот подробности:

Интерфейс может оставаться одинаковым для всех платформ:

Foo extends Library {
    void foo();
}

Просто добавьте отображение функции StdCallLibrary.FUNCTION_MAPPER:

Map<String, ?> options = Collections.singletonMap(
    Library.OPTION_FUNCTION_MAPPER,
    StdCallLibrary.FUNCTION_MAPPER
);
Foo proxy = Native.loadLibrary("foo", Foo.class, options);

У меня работает с JNA 4.5.1 в 32-битной Windows 7, Mac OS 10.13.2, 64-битной Unbuntu Linux 16.04. Я еще не тестировал другие платформы и сам не компилировал нативную библиотеку, поэтому ваш пробег может отличаться.


Вот еще больше деталей:

Изначально мой интерфейс выглядел так:

Foo extends Library {
    void foo();
}

и я попытался загрузить нативную библиотеку так:

Native.loadLibrary("foo", Foo.class);

Работает на Mac и Linux, но не на Windows 7 32-разрядная: Ошибка при поиске функции 'foo': указанная процедура не найдена.

Итак, я изменил свой интерфейс:

Foo extends StdCallLibrary {
    void foo();
}

и я попытался загрузить библиотеку с помощью специального сопоставителя функций stdcall:

Map<String, ?> options = Collections.singletonMap(
    Library.OPTION_FUNCTION_MAPPER,
    StdCallLibrary.FUNCTION_MAPPER
);
Foo proxy = Native.loadLibrary("foo", Foo.class, options);

Теперь он работает в 32-разрядной Windows 7, но не в Mac или Linux: Нераспознанное соглашение о вызовах: 63 : - (

Я думал, что мне понадобится отдельный путь к коду для каждой платформы, возможно, даже динамически добавим интерфейс Library или StdCallLibrary (с другим Proxy), но потом я обнаружил, что мы можем пообедать и поесть это тоже! Смотри выше.

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

...