JNA:找不到指定的程序

发布于 2024-10-20 07:12:57 字数 4431 浏览 4 评论 0原文

我试图了解 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 对象 strong>

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

I was trying to learn how JNA works, so I decided to use the spotify API (libspotify 0.0.7). I managed to load my dll correctly, but then it looks like my code is not finding any of the method defined in the API.

Here is my code:

My main file:

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

My Library interface:

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

My sp_session Object (opaque C struct)

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

My sp_session_config object

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

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

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

The C++ declaration of the method I'm trying to run

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

海未深 2024-10-27 07:12:58

终于通过使用 Dependency Walker 打开 libspotify.dll 找到了解决方案:
编译器向方法名称添加了一些额外信息(下划线前缀和@4或@8后缀)。

我必须:

  • 创建 FunctionMapper 的实现,根据真实名称重命名我的所有方法(在 Dependency Walker 中可用)
  • 使用选项映射中此映射器的实例实例化我的库。

I finally found the solution by opening the libspotify.dll with Dependency Walker:
The compiler added some extra information to the method name (a underscore prefix and a @4 or @8 suffix).

I had to:

  • Create an implementation of FunctionMapper that renamed all my methods according to the real names (available in Dependency Walker)
  • Instantiate my Library with an instance of this mapper in the options map.
梦中的蝴蝶 2024-10-27 07:12:58
    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”的五行定义)中遇到错误?

    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?

If you don't have access to it, neither does JNA. If it's an opaque type, look for functions to create, modify and delete it.

Also, did you get an error on the preceding statement, the five-line definition of the Java variable "artist"?

沉鱼一梦 2024-10-27 07:12:58

@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 上的 JNA 4.5.1。 2、Unbuntu Linux 16.04 64位。我还没有测试过其他平台,也没有自己编译原生库,所以你的情况可能会有所不同。


这里还有更多细节:

最初,我的界面如下所示:

Foo extends Library {
    void foo();
}

我尝试像这样加载本机库:

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

Worked on Mac and Linux, but not on Windows 7 32-bit: ErrorLooking up function '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 :-(

我认为每个平台都需要不同的代码路径,甚至可能添加 LibraryStdCallLibrary动态接口(与另一个Proxy),但后来我发现我们也可以吃午饭了!参见上面,

但我不确定这个特定行为是由 JNA 还是更确切地说是由 JNA 指定的。幸运的是,JNA 的下一个版本可能会改变,无论如何,这对我来说已经足够了。

@technomage's comment is very helpful. Here are the details:

The interface can remain the same for all platforms:

Foo extends Library {
    void foo();
}

Just add the StdCallLibrary.FUNCTION_MAPPER function mapper:

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

Works for me with JNA 4.5.1 on Windows 7 32-bit, Mac OS 10.13.2, Unbuntu Linux 16.04 64-bit. I haven't tested other platforms yet, and I didn't compile the native library myself, so your mileage may vary.


Here are even more details:

Initially, my interface looked like this:

Foo extends Library {
    void foo();
}

and I tried to load the native library like this:

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

Worked on Mac and Linux, but not on Windows 7 32-bit: Error looking up function 'foo': The specified procedure could not be found.

So I changed my interface:

Foo extends StdCallLibrary {
    void foo();
}

and I tried to load the library with the stdcall specific function mapper:

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

Now it worked on Windows 7 32-bit, but not on Mac or Linux: Unrecognized calling convention: 63 :-(

I thought I'd need a different code path for each platform, maybe even add the Library or StdCallLibrary interface dynamically (with another Proxy), but then I found that we can have our lunch and eat it too! See above.

I'm not sure though if this particular behavior is specified by JNA or rather a lucky accident that may change with the next release of JNA. Anyway, it's good enough for me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文