2011-03-01 5 views
3

J'essayais d'apprendre comment fonctionne JNA, j'ai donc décidé d'utiliser l'API Spotify (libspotify 0.0.7). J'ai réussi à charger ma DLL correctement, mais il semblerait que mon code ne trouve aucune méthode définie dans l'API.JNA: La procédure spécifiée n'a pas pu être trouvée

Voici mon code:

Mon fichier principal:

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); 
} 
} 

mon interface bibliothèque:

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); 
} 

Mon sp_session objet (struct opaque C)

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

Mon objet 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; 
} 

Mon 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 
} 

Mon exception Stack Trace

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) 

Le C de la déclaration de la méthode que je suis en train de courir

/** 
* 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); 

Répondre

2

Je enfin trouvé la solution en ouvrant le libspotify.dll avec Dependency Walker: Le compilateur a ajouté des informations supplémentaires au nom de la méthode (un préfixe et un trait de soulignement @ 4 ou @ 8 suffixe).

je dû:

  • Créer une implémentation de FunctionMapper que renommé toutes mes méthodes selon les vrais noms (disponible dans Dependency Walker)
  • instancier ma bibliothèque avec une instance de ce cartographe dans les options carte.
+0

Ah. L'en-tête déclare les méthodes avec _stdcall. Je suis habitué à _cdecl. Il y a plus sur le nom-mangling à http://en.wikipedia.org/wiki/Name_mangling. –

+3

JNA est livré avec un StdCallFunctionMapper qui gère le nom __stdcall mangling pour vous. Vous devez également vous assurer que votre bibliothèque implémente StdCallLibrary, ce qui indique à JNA d'utiliser la convention stdcall pour cette bibliothèque. – technomage

1
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? 

Si vous n'avez pas accès, ni ne la JNA. Si c'est un type opaque, cherchez des fonctions pour le créer, le modifier et le supprimer.

Également, avez-vous reçu une erreur sur l'instruction précédente, la définition à cinq lignes de la variable Java "artist"?

+0

Oui, il s'agit d'un type opaque et je ne trouve aucune méthode de création pour l'objet sp_artist. Les seules méthodes que j'ai trouvées étaient celles de la liste # 4. Pensez-vous que l'erreur est due au fait que l'objet Java que je crée ne correspond pas à l'objet C sp_artist? Et non, je n'ai pas eu d'erreur dans la définition de l'artiste. Je vous remercie! – nbarraille

+1

@nathanb - Je pense que cela conduirait probablement à des données parasites et à des écrasements de mémoire potentiels sur le côté C. J'ai jeté un coup d'oeil à l'API Spotify. Comme vous l'avez dit, sp_artist est intentionnellement opaque. Une instance peut être obtenue à partir de méthodes qui renvoient un sp_artist * - recherchez "SP_LIBEXPORT (sp_artist *)". D'un coup d'oeil, on dirait que la première fonction que vous appelez serait sp_session_create(), qui prend une structure non-opaque. Cela pourrait être un endroit plus facile à démarrer. BTW, JNA fournit un type de pointeur. –

+0

Merci d'avoir pris le temps de vérifier l'API. Je suis d'accord que je devrais commencer comme prévu par l'API en créant une session, donc j'ai changé mon code pour le faire (j'ai édité mon premier post). Mais j'ai toujours la même erreur. Une idée? Merci! – nbarraille

0

@technomagecomment est très utile.Voici les détails:

L'interface peut rester la même pour toutes les plateformes:

Foo extends Library { 
    void foo(); 
} 

Ajoutez juste le mappeur de fonction StdCallLibrary.FUNCTION_MAPPER:

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

Works pour moi avec la JNA sur Windows 4.5.1 7 32 bits, Mac OS 10.13.2, Unbuntu Linux 16.04 64 bits. Je n'ai pas encore testé d'autres plates-formes, et je n'ai pas compilé la bibliothèque native moi-même, donc votre kilométrage peut varier.


Voici encore plus de détails:

Dans un premier temps, mon interface ressemble à ceci:

Foo extends Library { 
    void foo(); 
} 

et j'ai essayé de charger la bibliothèque native comme ceci:

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

Travaillé sur Mac et Linux, mais pas sur Windows 7 32 bits: Erreur lors de la recherche de la fonction 'foo': Le procédure spécifiée n'a pas pu être trouvée.

Je changé mon interface:

Foo extends StdCallLibrary { 
    void foo(); 
} 

et j'ai essayé de charger la bibliothèque avec la fonction spécifique stdcall Mapper:

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

Maintenant, il a travaillé sur Windows 7 32 bits, mais pas sur Mac ou Linux: Convention d'appel non reconnue: 63 :-(

Je pensais que j'aurais besoin d'un différent chemin de code ent pour chaque plate-forme, peut-être même ajouter l'interface Library ou StdCallLibrary dynamiquement (avec un autre Proxy), mais alors j'ai trouvé que nous pouvons avoir notre déjeuner et le manger aussi! Voir au dessus.

Je ne suis pas sûr si ce comportement particulier est spécifié par JNA ou plutôt un accident chanceux qui peut changer avec la prochaine version de JNA. En tout cas, c'est assez bon pour moi.

Questions connexes