2011-03-01 283 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的下一個版本而改變。無論如何,這對我來說已經夠好了。