项目巴拿马:找不到函数

huangapple go评论72阅读模式
英文:

Project Panama: can't find function

问题

我正在尝试在Java 19中使用Project Panama(外部函数接口),使用以下代码在Windows 10上读取当前目录(作为一个简单的示例)。这是我的代码的开头:

final Linker linker = Linker.nativeLinker();
final SymbolLookup linkerLookup = linker.defaultLookup();
final SymbolLookup systemLookup = SymbolLookup.loaderLookup();
final SymbolLookup symbolLookup = name -> systemLookup.lookup(name)
                                 .or(() -> linkerLookup.lookup(name));
final MemorySegment addr = symbolLookup.lookup("GetCurrentDirectory")
                          .orElse(null);
...

问题是,在调试时,addr的值为null,因此看起来它无法按此名称找到该方法。我是否需要以某种方式告诉查找在哪个DLL中查找此函数(对于printf的测试,这是不必要的)?

英文:

I'm trying Project Panama (foreign function interface) of Java 19 with following code to read the current directory on Windows 10 (as a simple example). This is the begin of my code:

final Linker linker = Linker.nativeLinker();
final SymbolLookup linkerLookup = linker.defaultLookup();
final SymbolLookup systemLookup = SymbolLookup.loaderLookup();
final SymbolLookup symbolLookup = name -> systemLookup.lookup(name)
                                 .or(() -> linkerLookup.lookup(name));
final MemorySegment addr = symbolLookup.lookup("GetCurrentDirectory")
                          .orElse(null);
...

The problem is, when debugging addr is null, so it looks like it can't find the method by this name. Do I somehow need to tell the lookup in what DLL it should look for this function (for a test with printf this was not necessary)?

答案1

得分: 4

是的,您需要加载包含GetCurrentDirectory函数的库,它是Kernel32库(根据文档):

System.loadLibrary("Kernel32");

在使用System::loadSystem::loadLibrary加载库后,可以使用加载器查找(即SymbolLookup.loaderLookup())找到它。参考文档

返回与调用者的类加载器关联的库中的符号查找。

当库通过在由CL定义的类中的代码中调用System.load(String)System.loadLibrary(String)来加载时,库与类加载器CL关联。

请注意,System::loadLibrary还要求包含库的目录在java.library.path系统属性中列出(如果没有,将会抛出异常)。对于Kernel32,这应该是C:\Windows\System32

关于printf之所以能够工作的原因是,您使用了nativeLinkerdefaultLookup,它可以找到标准C库中的所有符号。不过,GetCurrentDirectory不属于该库。

此外,看起来GetCurrentDirectory被定义为__inline,这意味着您不能以动态方式链接到它,因为根据我所知,Kernel32.dll中根本没有这个符号/函数:

dumpbin.exe /EXPORTS C:\Windows\System32\kernel32.dll | sls GetCurrentDirectory

555  22A 0001F010 GetCurrentDirectoryA
556  22B 00018CA0 GetCurrentDirectoryW

因此,您需要直接查找并调用GetCurrentDirectoryA(用于char)或GetCurrentDirectoryW(用于wchar_t):

System.loadLibrary("Kernel32");
SymbolLookup loaderSyms = SymbolLookup.loaderLookup();
MemorySegment addr = loaderSyms.lookup("GetCurrentDirectoryA").orElseThrow();
// 使用'addr'

希望这些信息对您有所帮助。

英文:

> Do I somehow need to tell the lookup in what DLL it should look for this function

Yes, you have to load the library that contains GetCurrentDirectory, which is Kernel32 (according to the documentation):

System.loadLibrary("Kernel32");

After loading a library with System::load or System::loadLibrary, it can be found using the loader lookup (i.e. SymbolLookup.loaderLookup()). See the documentation:

> Returns a symbol lookup for symbols in the libraries associated with
> the caller's class loader.
>
> A library is associated with a class loader CL when the library is
> loaded via an invocation of System.load(String) or
> System.loadLibrary(String) from code in a class defined by CL. ...

Note that System::loadLibrary also requires the directory that contains the library to be listed in the java.library.path system property (you will get an exception if it's not). For Kernel32 this should be C:\Windows\System32

> (for a test with printf this was not necessary)?

The reason it works for printf is because you are using the defaultLookup of the nativeLinker which can find all the symbols in the standard C library. GetCurrentDirectory is not a part of that library though.


Furthermore, it looks like GetCurrentDirectory is defined as __inline, which means you can not link against it dynamically, as the symbol/function is simply not present in Kernel32.dll, AFAICT:

> dumpbin.exe /EXPORTS C:\Windows\System32\kernel32.dll | sls GetCurrentDirectory

        555  22A 0001F010 GetCurrentDirectoryA
        556  22B 00018CA0 GetCurrentDirectoryW

Instead, you have to look up and call GetCurrentDirectoryA for chars or GetCurrentDirectoryW for wchar_ts directly:

System.loadLibrary("Kernel32");
SymbolLookup loaderSyms = SymbolLookup.loaderLookup();
MemorySegment addr = loaderSyms.lookup("GetCurrentDirectoryA").orElseThrow();
// use 'addr'

huangapple
  • 本文由 发表于 2023年2月27日 03:39:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/75574571.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定