[英]JNA: The specified procedure could not be found
我試圖了解JNA的工作原理,所以我決定使用spotify API(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枚舉
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);
我終於通過使用Dependency Walker打開libspotify.dll找到了解決方案:編譯器在方法名稱中添加了一些額外信息(下划線前綴和@ 4或@ 8后綴)。
我不得不:
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”的五行定義?
@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);
適用於Windows 7 32位,Mac OS 10.13.2,64位Unbuntu Linux 16.04上的JNA 4.5.1。 我還沒有測試過其他平台,我自己也沒有編譯本地庫,所以你的里程可能會有所不同。
這里有更多細節:
最初,我的界面看起來像這樣:
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);
現在它適用於Windows 7 32位,但不適用於Mac或Linux: 無法識別的調用約定:63 :-(
我認為我需要為每個平台提供不同的代碼路徑,甚至可以動態地添加Library
或StdCallLibrary
接口(與另一個Proxy
),但后來我發現我們可以吃午餐並吃掉它! 往上看。
我不確定JNA是否指定了這種特殊行為,或者更確切地說是JNA的下一版本可能會發生的幸運事故。 無論如何,這對我來說已經足夠了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.