Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <blockquote> <p>Invoking a JVM from C++ which then executes the Java file (I think this is the most reasonable way but this needs a lot of code)</p> </blockquote> <p>Yes, it definitely is the most reasonable way. And with JNI and the <a href="http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/invocation.html#wp9502" rel="noreferrer">invocation API</a> it's not even that much code.</p> <h1>Finding the jvm.dll</h1> <p>You could try things like hardcoding the path to the Oracle JVM's <code>jvm.dll</code> or searching for a file called <code>jvm.dll</code> in the programs folder, but all that is obviously extremely hacky. However, there is apparently a pretty easy solution: The registry. The key <code>HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment</code> contains a <code>REG_SZ</code> called <code>CurrentVersion</code>. You can read the value of this key (currently it's <code>1.7</code>) and open a child key with that name (<code>HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.7</code> in this example). That key will then contain a <code>REG_SZ</code> called <code>RuntimeLib</code> which is the path to your <code>jvm.dll</code>. Don't worry about <code>Program files</code> vs <code>Program files (x86)</code>. WOW64 will automatically redirect your registry query to <code>HKLM\SOFTWARE\Wow6432Node</code> if you're a 32bit process on a 64bit windows and that key contains the path to the 32 bit <code>jvm.dll</code>. Code:</p> <pre><code>#include &lt;Windows.h&gt; #include &lt;jni.h&gt; // C:\Program Files\Java\jdk1.7.0_10\include\jni.h // ... DWORD retval; // fetch jvm.dll path from registry HKEY jKey; if (retval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\JavaSoft\\Java Runtime Environment"), 0, KEY_READ, &amp;jKey)) { RegCloseKey(jKey); // assuming you're using C++/CLI throw gcnew System::ComponentModel::Win32Exception(retval); } TCHAR versionString[16]; // version numbers shouldn't be longer than 16 chars DWORD bufsize = 16 * sizeof(TCHAR); if (retval = RegGetValue(jKey, NULL, TEXT("CurrentVersion"), RRF_RT_REG_SZ, NULL, versionString, &amp;bufsize)) { RegCloseKey(jKey); // assuming you're using C++/CLI throw gcnew System::ComponentModel::Win32Exception(retval); } TCHAR* dllpath = new TCHAR[512]; bufsize = 512 * sizeof(TCHAR); retval = RegGetValue(jKey, versionString, TEXT("RuntimeLib"), RRF_RT_REG_SZ, NULL, dllpath, &amp;bufsize) RegCloseKey(jKey); if (retval) { delete[] dllpath; // assuming you're using C++/CLI throw gcnew System::ComponentModel::Win32Exception(retval); } </code></pre> <h2>Loading the jvm.dll and getting the CreateJavaVM function</h2> <p>This part is pretty straightforward, you just use <code>LoadLibrary</code> and <code>GetProcAddress</code>:</p> <pre><code>HMODULE jniModule = LoadLibrary(dllpath); delete[] dllpath; if (jniModule == NULL) throw gcnew System::ComponentModel::Win32Exception(); typedef int (JNICALL * JNI_CreateJavaVM)(JavaVM** jvm, JNIEnv** env, JavaVMInitArgs* initargs); JNI_CreateJavaVM createJavaVM = (JNI_CreateJavaVM)GetProcAddress(jniModule, "JNI_CreateJavaVM"); </code></pre> <h2>Creating the JVM</h2> <p>Now you can invoke that function:</p> <pre><code>JavaVMInitArgs initArgs; initArgs.version = JNI_VERSION_1_6; initArgs.nOptions = 0; JavaVM* jvm; JNIEnv* env; if ((retval = createJavaVM(&amp;jvm, &amp;env, &amp;initArgs)) != JNI_OK) throw gcnew System::Exception(); // beyond the scope of this answer </code></pre> <p>Congratulations! There's now a JVM running right inside your process! You would probably launch the JVM at the startup of your application. Unless you are 100% <strong>sure</strong> that you will only ever invoke Java code from the thread that just created the JVM, you can throw away the <code>env</code> pointer, but you have to keep the <code>jvm</code> pointer.</p> <h2>Getting the JNI environment (optional)</h2> <p>So now you created the JVM and your application is up and running and then somebody clicks that button. Now you want to invoke Java code. If you are 100% <strong>sure</strong> that you are right now on the thread that created the JVM in the previous step and you still have the <code>env</code> pointer, then you can skip this. Otherwise, perform a quick check if the current thread is attached to the JVM and attach it if it isn't:</p> <pre><code>JNIEnv* env; bool mustDetach = false; jint retval = jvm-&gt;GetEnv((void**)&amp;env, JNI_VERSION_1_6); if (retval == JNI_EDETACHED) { JavaVMAttachArgs args; args.version = JNI_VERSION_1_6; args.name = NULL; args.group = NULL; retval = jvm-&gt;AttachCurrentThread(&amp;env, &amp;args); mustDetach = true; // to clean up afterwards } if (retval != JNI_OK) throw gcnew System::Exception(); // should never happen invokeJavaCode(env); // next step if (mustDetach) jvm-&gt;DetachCurrentThread(); </code></pre> <h2>Invoking Java code</h2> <p>Now you are right there, you want to invoke that Java code and you even have the <code>env</code> pointer. You want the easiest solution, so this is how you call a static method:</p> <pre><code>jclass clazz = env-&gt;FindClass("com/myself/MyClass"); if (clazz == NULL) throw gcnew System::Exception(); jmethodID mid = env-&gt;GetStaticMethodID(clazz, "myStaticMethod", "&lt;signature&gt;"); if (mid == NULL) throw gcnew System::Exception(); &lt;type&gt; returnedValue = env-&gt;CallStatic&lt;type&gt;Method(clazz, mid, &lt;arguments&gt;); </code></pre> <p>You can use <code>javap -s</code> (command line tool) to determine a method's signature. <code>&lt;type&gt;</code> can be any primitive type (it must match the return type of the Java method). The arguments can be of any primitive type, as long as they match the arguments of the Java method.</p> <h2>The end</h2> <p>And there you have it: The easiest way to invoke Java code from C++ on Windows (actually only the first two parts are windows-specific...). Oh, and also the most efficient one. Screw databases and files. Using <code>127.0.0.1</code> sockets would be an option but that's significantly less efficient and probably not less work than this. Wow, this answer is a bit longer than I expected. Hopefully it helps.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload