user6828073
user6828073

Reputation:

JNA: How use CreateProcess for execute 32 bit version of a system native app in 64 bit systems?

I have a application that uses JNA for execute any other application using CreateProcess api. This works very fine, but when i need execute a 32 bit version of a system native app, is executing 64 bit version ( present on SysWOW64 folder ), for example: notepad.exe 64 bit.

So, exist some way for solve this troube?

I had tried use Wow64DisableWow64FsRedirection but seem don't is working.

enter image description here

My code:

Execute class:

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);
    }

}

Main:

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");

}

Upvotes: 2

Views: 1389

Answers (2)

Govind Parmar
Govind Parmar

Reputation: 21562

This may not be appropriate for your set-up depending on what your needs are, but at my company we had this problem when we moved to a 64-bit operating system for our terminal server and we had an in-house Java application that required the 32-bit version of Java to run properly.

My solution was to go into the 64-bit java directory, rename java.exe and javaw.exe to java.exe.bak and javaw.exe.bak, respectively, then replace those files with stubs that launched the 32-bit version of Java instead. This was acceptable in our case since we only have one application that uses Java and we did not foresee ourselves writing any more applications in Java going forward:

// 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 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 = StringCchCatW(argstr, cchargstr, L" ");
        if (FAILED(hr)) break;
        
        hr = StringCchCatW(argstr, cchargstr, argv[i]);
        if (FAILED(hr)) break;

    }
    return hr;
}

int 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;
}

Upvotes: 0

Edwin Buck
Edwin Buck

Reputation: 70919

--- Updated because Windows is well, Windows ---

32 bit executables are in SysWOW64, while 64 bit executables are in System32. Apparently this bizarre choice makes sense to the people up in Redmond.

--- Rest of post follows, with updates ---

You are asking a 64 bit program to load and interface with 32 bit executables. The "compatibility" for 32 bit executables doesn't extend to linking them to 64 bit programs.

You need to launch a 32 bit JVM so the JNI interface will match the desired environment.

However, your example might not even need JNI. If you are launching 32-bit standalone programs, then you do not need to use JNI. Instead you could use ProcessBuilder and pass the command line arguments to the shell to effectively ensure you launch the 32 bit executable.

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

JNI is for when you need to link the JVM to native libraries, it is not the preferred way to launch native applications. ProcessBuilder is what you prefer to launch native applications.

Upvotes: 1

Related Questions