2011-03-01 287 views
3

我想了解JNA是如何工作的,所以我决定使用spotify API(libspotify 0.0.7)。我设法正确地加载我的dll,但是看起来好像我的代码没有找到在API中定义的任何方法。JNA:指定的程序找不到

这里是我的代码:

我的主文件:

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枚举

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

回答

2

最后通过打开与依赖沃克libspotify.dll找到了解决办法: 编译器增加了一些额外信息的方法名(下划线前缀和一个@ 4或8 @后缀)。

我只好:

  • 创建FunctionMapper是改名根据真实姓名我的所有方法的实现(Dependency Walker中可用)
  • 实例化的选项的这个映射器的情况下我的图书馆地图。
+0

啊。标题是用_stdcall声明方法。我习惯了_cdecl。在http://en.wikipedia.org/wiki/Name_mangling上有更多关于名称的文章。 – 2011-03-02 03:37:41

+3

JNA附带了一个StdCallFunctionMapper,它可以处理__stdcall名称。你还应该确保你的库实现了StdCallLibrary,它指示JNA使用该库的stdcall约定。 – technomage 2011-11-12 14:50:48

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? 

如果您没有访问它,同样没有JNA。如果它是不透明类型,请查找函数以创建,修改和删除它。

另外,你是否在前面的语句,Java变量“艺术家”的五行定义中出现错误?

+0

是的,它是一个不透明的类型,我找不到任何sp_artist对象的创建方法。我发现的唯一方法是列表#4中的方法。您认为错误是由于我创建的Java对象与C sp_artist对象不匹配吗?不,我没有在艺术家的定义中出现任何错误。谢谢! – nbarraille 2011-03-01 15:25:47

+1

@nathanb - 我认为这将更有可能导致垃圾数据和C端潜在的内存覆盖。我看了一眼spotify API。正如你所说,sp_artist是故意不透明的。可以从返回sp_artist *的方法获得实例 - 搜索“SP_LIBEXPORT(sp_artist *)”。一目了然,看起来像你会调用的第一个函数将是sp_session_create(),它采用一个不透明的结构。这可能是一个更容易开始的地方。顺便说一句,JNA提供了一个指针类型。 – 2011-03-01 18:28:34

+0

感谢您花时间检查API。我同意我应该通过创建会话来按API开始,所以我改变了我的代码(我编辑了我的第一篇文章)。但我仍然有同样的错误。任何想法?谢谢! – nbarraille 2011-03-01 19:56:46

0

@technomagecomment是非常有帮助的。下面是详细信息:

该接口可以保持相同的所有平台:用JNA 4.5.1在Windows

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

工作对我来说:

Foo extends Library { 
    void foo(); 
} 

只需添加StdCallLibrary.FUNCTION_MAPPER函数映射器7 32位,Mac OS 10.13.2,Unbuntu Linux 16.04 64位。我还没有测试过其他平台,而且我自己也没有编译本地库,所以你的里程可能会有所不同。


这里有更详细信息:

最初,我的界面是这样的:

Foo extends Library { 
    void foo(); 
} 

,我试图加载这样的本地库:

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

在Mac和Linux上工作,但不在Windows 7 32位上:查找函数'foo'时出错:The指定的程序无法找到。

所以我改变了我的界面:

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

现在它工作在Windows 7 32位,但不在Mac或Linux上:无法识别的通话约定:63 :-(

我以为我需要一个不同的每个平台的代码路径,甚至可能动态地添加LibraryStdCallLibrary接口(与另一个Proxy),但后来我发现我们可以吃我们的午餐并且也吃它!往上看。

我不确定,如果这个特定的行为是由JNA指定的,或者是一个幸运的事故,可能随着JNA的下一个版本而改变。无论如何,这对我来说已经够好了。