使用 jna 获取 IShellFolder

Get IShellFolder with jna

我是 java jna API 的新手,我遇到了 Win32 API 方法的问题。我需要从 Win32 API 获取 IShellFolder 对象,因为我需要文件的 IContextMenu 对象。但是当我从 jna ParseDisplayName 调用 Win32 API 方法时,我得到了相同的结果错误: "The system cannot find the file specified"。我在下面附上了我的代码。

        String directory = "c:\Users";

        IntByReference pchEaten = new IntByReference();
        pchEaten.setValue(0);
        PointerByReference ppidl = new PointerByReference();
        IntByReference pdwAttributes = new IntByReference();
        pdwAttributes.setValue(0);
        PointerByReference desktopFolder = new PointerByReference();
        WinNT.HRESULT hResult = Shell32.INSTANCE.SHGetDesktopFolder(desktopFolder);
        IShellFolder shellFolder = IShellFolder.Converter.PointerToIShellFolder(desktopFolder);
        if (COMUtils.SUCCEEDED(hResult) && shellFolder != null) {
            hResult = shellFolder.ParseDisplayName(new WinDef.HWND(Pointer.NULL), Pointer.NULL, directory, pchEaten, ppidl, pdwAttributes);
            if (COMUtils.SUCCEEDED(hResult)) {
                return true;
            }
            else {
                return false;
            }
        }
        else {
            return false;
        }

我测试过的每个目录都有同样的错误。 非常感谢。

欢迎使用 Whosebug!

正如我在评论中提到的,iShellFolder::ParseDisplayNamedocumentation 表示第三个参数 pszDisplayName 是 "A null-terminated Unicode string with the display name." 当在 Windows 文档,"UTF-16" 字符集已实现。

JNA 中 Windows Unicode 字符串的正确映射是 WString。不幸的是,JNA 已将此参数映射为单字节 UTF-8 编码的 String(即使 Java 在内部以 UTF-16 存储字符串)。

您可以通过创建更长的 String 将 UTF-8 转换为 UTF-16(小端)编码(以字节为单位)来解决此问题。我之前发布了一个利用其已知编码的 ASCII 解决方案,但更好的解决方案是使用 String 的方法将字节提取为 UTF-16LE,然后从这些字节中创建一个 String 对象。此代码进行转换:

String test = "müller_test.pdf";
byte[] utf16 = test.getBytes("UTF-16LE");
System.out.println("utf16 that we need to pass:\n" + Arrays.toString(utf16));
String wString = new String(utf16);
System.out.println("Pass this to Windows:\n" + wString);

产生此输出:

utf16 (little endian) that we need to pass:
[99, 0, 58, 0, 92, 0, 85, 0, 115, 0, 101, 0, 114, 0, 115, 0]
Pass this to Windows:
m � l l e r _ t e s t . p d f

不幸的是,通过检查字节我不确定它是否包含空终止符,因此您可能希望在执行此操作之前将 '\u0000' 字符添加到字符串的末尾。

所以最终你会得到这样的一行:

String wstring = new String((test + '\u0000').getBytes("UTF-16LE"));

wstring 传递给 Windows 它需要 Unicode 字符串的地方,一切就绪。