英文:
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::load
或System::loadLibrary
加载库后,可以使用加载器查找(即SymbolLookup.loaderLookup()
)找到它。参考文档:
返回与调用者的类加载器关联的库中的符号查找。
当库通过在由
CL
定义的类中的代码中调用System.load(String)
或System.loadLibrary(String)
来加载时,库与类加载器CL
关联。
请注意,System::loadLibrary
还要求包含库的目录在java.library.path
系统属性中列出(如果没有,将会抛出异常)。对于Kernel32
,这应该是C:\Windows\System32
。
关于printf
之所以能够工作的原因是,您使用了nativeLinker
的defaultLookup
,它可以找到标准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 char
s or GetCurrentDirectoryW
for wchar_t
s directly:
System.loadLibrary("Kernel32");
SymbolLookup loaderSyms = SymbolLookup.loaderLookup();
MemorySegment addr = loaderSyms.lookup("GetCurrentDirectoryA").orElseThrow();
// use 'addr'
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论