JNA:如何使用CreateProcess在64位系统中执行32位版本的系统本机应用程序?

时间:2016-11-11 15:56:36

标签: java windows jna

我有一个使用JNA的应用程序,可以使用CreateProcess api执行任何其他应用程序。这非常好,但是当我需要执行32位版本的系统本机应用程序时,正在执行64位版本(存在于SysWOW64文件夹中),例如:notepad.exe 64位。

那么,是否存在解决这个问题的方法?

我曾尝试使用Wow64DisableWow64FsRedirection,但似乎没有用。

enter image description here

我的代码:

执行课程:

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.WString;
import static com.sun.jna.platform.win32.WinBase.STARTF_USESHOWWINDOW;
import static com.sun.jna.platform.win32.WinUser.SW_HIDE;
import com.sun.jna.win32.StdCallLibrary;
import java.util.Arrays;
import java.util.List;


public class Execute {

    public interface Kernel32 extends StdCallLibrary {
    Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);

    boolean CreateProcessA(
             String lpApplicationName
            , String lpCommandLine
            , Structure lpProcessAttributes
            , Structure lpThreadAttributes
            , boolean bInheritHandles
            , int dwCreationFlags
            , Structure lpEnvironment
            , String lpCurrentDirectory
            , Structure lpStartupInfo
            , Structure lpProcessInformation);
}

public static class ProcessInformation extends Structure {
    public Pointer hProcess;
    public Pointer hThread;
    public int dwProcessId;
    public int dwThreadId;

      @Override
      protected List getFieldOrder() {
      return Arrays.asList(new String[] { "hProcess", "hThread", "dwProcessId", "dwThreadId" });
  }
    }

public static class StartupInfoA extends Structure {
    public int cb;
    public WString lpReserved;
    public WString lpDesktop;
    public WString lpTitle;
    public int dwX;
    public int dwY;
    public int dwXSize;
    public int dwYSize;
    public int dwXCountChars;
    public int dwYCountChars;
    public int dwFillAttribute;
    public int dwFlags;
    public short wShowWindow;
    public short cbReserved2;
    public Pointer lpReserved2;
    public Pointer hStdInput;
    public Pointer hStdOutput;
    public Pointer hStdError;

        @Override
        protected List getFieldOrder() {
        return Arrays.asList(new String[] { "cb", "lpReserved", "lpDesktop", "lpTitle", "dwX", "dwY", "dwXSize", "dwYSize", "dwXCountChars", "dwYCountChars", "dwFillAttribute", "dwFlags", "wShowWindow", "cbReserved2", "lpReserved2", "hStdInput", "hStdOutput", "hStdError" });
  }
    }

public static void ExecuteProc(String software){

        ProcessInformation processInformation = new ProcessInformation();
        StartupInfoA startupInfo = new StartupInfoA(); 
        startupInfo.dwFlags = STARTF_USESHOWWINDOW;
        startupInfo.wShowWindow = SW_HIDE;

        Kernel32.INSTANCE.CreateProcessA(software, null
                , null
                , null
                , true
                , 0
                , null
                , "C:\\Windows\\System32\\"
                , startupInfo
                , processInformation);
    }

}

主:

public static native boolean Wow64DisableWow64FsRedirection(PointerByReference OldValue);

public static void Exec() {

       PointerByReference lpBuffer = new PointerByReference();

       Wow64DisableWow64FsRedirection(lpBuffer); // fails here

       String sysdir = System.getenv("WINDIR") + "\\System32\\";
       ExecuteProc(sysdir + "notepad.exe");

}

2 个答案:

答案 0 :(得分:0)

---更新因为Windows很好,Windows ---

32位可执行文件位于SysWOW64中,而64位可执行文件位于System32中。显然,这种奇怪的选择对雷德蒙德的人们来说是有意义的。

---其余的帖子如下,更新---

您要求64位程序加载并与32位可执行文件接口。 32位可执行文件的“兼容性”并未扩展到将它们链接到64位程序。

您需要启动32位JVM,以便JNI接口匹配所需的环境。

但是,您的示例可能甚至不需要JNI。如果要启动32位独立程序,则不需要使用JNI。相反,您可以使用ProcessBuilder并将命令行参数传递给shell,以有效地确保您启动32位可执行文件。

ProcessBuilder pb = new ProcessBuider(String.format("%s\\SysWOW64\\notepad.exe", System.getEnv("WINDIR"));
Process process = pb.start();

JNI适用于需要将JVM链接到本机库的情况,它不是启动本机应用程序的首选方式。您希望ProcessBuilder启动本机应用程序。

答案 1 :(得分:0)

这可能不适合您的设置,具体取决于您的需求,但在我的公司,当我们为我们的终端服务器迁移到64位操作系统并且我们有一个内部Java时,我们遇到了这个问题需要32位版本的Java才能正常运行的应用程序。

我的解决方案是进入64位java目录,分别将java.exejavaw.exe重命名为java.exe.bakjavaw.exe.bak,然后用存根替换那些文件推出了32位版本的Java。这在我们的案例中是可以接受的,因为我们只有一个使用Java的应用程序,我们没有预见到自己将在Java中编写更多的应用程序:

// Stub file that launches the 32-bit java.exe
// Intended to replace the 64-bit java executable to ensure that (OUR APPLICATION) which only works with 32-bit java works correctly
// Author: Govind Parmar

#include <Windows.h>
#include <strsafe.h>

HRESULT __cdecl cat_argv(int argc, WCHAR *argv[], WCHAR *argstr, int cchargstr)
{
    int i;
    HRESULT hr;
    ZeroMemory(argstr, sizeof(WCHAR) * cchargstr);

    // first arg is program name; start with i=1 instead of 0
    for (i = 1; i < argc; i++)
    {
        // Space out arguments
        hr = StringCchCat(argstr, cchargstr, L" ");
        if (FAILED(hr)) break;

        hr = StringCchCat(argstr, cchargstr, argv[i]);
        if (FAILED(hr)) break;

    }
    return hr;
}

int __cdecl wmain(int argc, WCHAR *argv[])
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    WCHAR args[8192];
    DWORD dwExit;
    ZeroMemory(&si, sizeof(STARTUPINFO));
    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

    si.cb = sizeof(STARTUPINFO);
    if (FAILED(cat_argv(argc, argv, args, 8192)))
    {
        _putws(L"Could not concatenate argument string!");
        return 0;
    }

    CreateProcess(L"C:\\Program Files (x86)\\Java\\jre1.8.0_101\\bin\\java.exe", args, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, NULL, L"C:\\Program Files (x86)\\Java\\jre1.8.0_101\\bin\\", &si, &pi);

    WaitForSingleObject(pi.hProcess, INFINITE);
    GetExitCodeProcess(pi.hProcess, &dwExit);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return dwExit;
}