【问题标题】:What is the easiest way to call a Windows kernel function from Java?从 Java 调用 Windows 内核函数的最简单方法是什么?
【发布时间】:2010-11-03 07:08:15
【问题描述】:

在搜索如何执行此操作时,我发现一些关于不同选项的模糊讨论,例如 JNI 与 JNA,但具体示例不多。

上下文:如果 Java 的 File.renameTo() 不能完成它的工作(无论出于何种原因;it is a little problematic),我想直接使用这个在 kernel32.dll 中定义的原生 Windows 函数(from this answer ):

BOOL WINAPI MoveFile(
  __in  LPCTSTR lpExistingFileName,
  __in  LPCTSTR lpNewFileName
);

那么,无论使用何种方法,您将如何在 Java 代码中准确调用该函数?我正在寻找最简单的方法,使用最少的非 Java 代码或额外步骤(例如在编译或部署中)。

【问题讨论】:

    标签: java windows winapi java-native-interface jna


    【解决方案1】:

    如果这确实是必要的(renameTo 不起作用并且您确定 MoveFile 会起作用),我会使用 JNA。看起来大部分工作已经在 com.mucommander.file.util.Kernel32.java/Kernel32API.java 中完成。

    【讨论】:

    • 并且不要忘记 GetLastError() 以获得与 File.renameTo() 不同的失败移动的有意义的解释
    • 我不确定 /sure/ MoveFile 会起作用,但我可以尝试,因为 renameTo 肯定不会。顺便说一句,您链接到的两个文件不能完美地组合在一起;缺少 Kernel32API.DEFAULT_OPTIONS。您应该将什么作为第三个参数传递给 Native.loadLibrary?我正在尝试使用空地图,但无法正常工作; “ClassCastException: $Proxy0 cannot be cast to com.sun.jna.Library”发生。
    • @jitter,我只是看看
    • Jonik,我已经修复了指向最新版本的链接,所以它们应该是同步的。请注意 DEFAULT_OPTIONS 在 com.sun.jna.examples.win32.W32API (jna.dev.java.net/javadoc/com/sun/jna/examples/win32/…) 中定义,Kernel32API 继承自该 API。
    【解决方案2】:

    基于NativeCall library,我做了以下POC Application

    它使用 kernel32.dll

    中的 MoveFileA 函数

    它是完整的工作示例,带有一个 run.bat 和所有 jar 和 dll。

    它将包含的 test.txt 移动到 test2.txt


    如果您不喜欢 NativeCall 库版本,我会在 Java Native Access (JNA) 库的基础上再做一个 POC Application。这次MoveFileAMoveFileW实现并演示了。

    【讨论】:

    • 谢谢!使用 NativeCall,我能够从 Java 中快速移动大型目录(尽管我还没有在 renameTo 经常失败的情况下尝试过),并且进行调用的代码确实很简单。但是我现在对 NativeCall.dll 文件有一些疑问 - 如果您需要始终将它放在工作目录中(如果它在其中一个罐子内,对 API 的用户透明地使用,嵌入 lib 将更容易)
    • 由于您不喜欢 NativeCall 变体,我也做了一个 JNA POC 应用程序。同时实现 MoveFileA 和 MoveFileW
    【解决方案3】:

    如果您使用 JNA,请考虑直接调用 MoveFileW - 这样就不必提供配置信息以在 Unicode 和 ANSI 调用之间进行选择。

    import java.io.*;
    import com.sun.jna.*;
    
    public class Ren {
    
      static interface Kernel32 extends Library {
        public static Kernel32 INSTANCE = (Kernel32) Native
            .loadLibrary("Kernel32", Kernel32.class);
    
        public static int FORMAT_MESSAGE_FROM_SYSTEM = 4096;
        public static int FORMAT_MESSAGE_IGNORE_INSERTS = 512;
    
        public boolean MoveFileW(WString lpExistingFileName,
            WString lpNewFileName);
    
        public int GetLastError();
    
        public int FormatMessageW(int dwFlags,
            Pointer lpSource, int dwMessageId,
            int dwLanguageId, char[] lpBuffer, int nSize,
            Pointer Arguments);
      }
    
      public static String getLastError() {
        int dwMessageId = Kernel32.INSTANCE.GetLastError();
        char[] lpBuffer = new char[1024];
        int lenW = Kernel32.INSTANCE.FormatMessageW(
            Kernel32.FORMAT_MESSAGE_FROM_SYSTEM
                | Kernel32.FORMAT_MESSAGE_IGNORE_INSERTS, null,
            dwMessageId, 0, lpBuffer, lpBuffer.length, null);
        return new String(lpBuffer, 0, lenW);
      }
    
      public static void main(String[] args) throws IOException {
        String from = ".\\from.txt";
        String to = ".\\to.txt";
        new FileOutputStream(from).close();
        if (!Kernel32.INSTANCE.MoveFileW(new WString(from),
            new WString(to))) {
          throw new IOException(getLastError());
        }
      }
    }
    

    编辑:我在检查代码后编辑了我的答案 - 我误认为在签名中使用 char[] - 最好使用 WString

    【讨论】:

    • 好的,但是如何直接调用MoveFileW呢?就像 Matthew Flaschen 建议的那样,还是什么?几行示例代码(例如如何使用 Native.loadLibrary() 等)将不胜感激。
    • 感谢 JNA 示例!顺便说一句,界面将如何寻找 MoveFileA(在 jitter 的回答中提到)?简单地使用“MoveFileA”而不是“MoveFileW”是不好的。我真的在尝试移动/重命名一个目录,但无法使用它 - 虽然我不确定这是因为 MoveFileW 还是其他原因。
    • 该函数可以定义为 MoveFileA(String,String),但这样做会限制您可以使用的文件名。您可能无法在系统上的所有文件上使用它,尤其是。如果您使用的是 NTFS。我在一个目录上测试了上面的代码,所以你的问题可能是别的。
    • 我接受这个,因为我认为带有示例代码的解决方案应该就在这里。 (当然,如果发布了更好/更简单的方法,我会更改它。:)
    猜你喜欢
    • 1970-01-01
    • 2022-08-13
    • 2013-06-24
    • 1970-01-01
    • 2013-06-25
    • 2021-11-11
    相关资源
    最近更新 更多