📄 delphi-jni-1.html
字号:
<blockquote>
<p>
The majority of the methods declared in <a href="src/jni_pas.html">jni.pas </a> are methods of a this <b><tt>record</tt></b>:
<pre>
JNINativeInterface_ = <b>packed record</b>
reserved0 : Pointer;
reserved1 : Pointer;
reserved2 : Pointer;
reserved3 : Pointer;
GetVersion : <b>function</b>(Env: PJNIEnv): JInt; <b>stdcall</b>;
<font color="#003399"><i> (* . . . Over 200 additional methods declared here . . . *)</i></font>
<b>end;</b>
</pre>
Every translation of the C/C++ header file <a href="src/jni_h.html">jni.h</a> <i>must</i> declare the methods of a <b><tt>record</tt></b>
in the <i>exact</i> same order.
<p>Note that the order of the <tt>records</tt> in the Delphi file is <i>not</i> important. Only the order of the methods <i>within</i>
the <tt>records</tt> is important.
</BLOCKQUOTE>
<p>
<a href="#Top">Back to top</a>
</blockquote>
<div class="SectionHeader">
<A NAME="Example">A <i>HelloWorld</i> Example</A>
</div>
<p>
<BLOCKQUOTE>
One of the best ways to learn a new programming skill is by example. In
keeping with tradition, I present the canonical programming example that
simply prints the words "Hello World!" to the display. The twist is that
Java code will invoke a native function to do the actual printing via the
Delphi library function, <tt>WriteLn</tt>.
<p>
<b>1. Create the Java files.</b>
First, create the two Java files as shown below.
<BLOCKQUOTE>
<i><A HREF="../Demos/Part1/HelloWorld.java">HelloWorld.java</a></i>:
<pre class="sourcecode"><code><b>class</b> HelloWorld
{
<b>public</b> <b>native</b> <b>void</b> displayHelloWorld();
<b>static</b>
{
System.loadLibrary(<font color="#9933CC">"HelloWorldImpl"</font>);
}
}
</code></pre>
<i><A HREF="../Demos/Part1/Main.java">Main.java</a></i>:
<pre class="sourcecode"><code><b>class</b> Main
{
<b>public</b> <b>static</b> <b>void</b> main(String[] args)
{
HelloWorld hw = <b>new</b> HelloWorld();
hw.displayHelloWorld();
}
}
</code></pre>
</BLOCKQUOTE>
<b>2. Compile the Java files. </b>
<BLOCKQUOTE>
<pre>
javac HelloWorld.java
javac Main.java
</pre>
</BLOCKQUOTE>
<b>3. Create and build the Delphi file.</b>
You will end up with a file called <tt>HelloWorldImpl.dll</tt>.
<BLOCKQUOTE>
<i><A HREF="src/HelloWorldImpl_dpr.html">HelloWorldImpl.dpr</a></i>:
<pre class="sourcecode"><code><b>library</b> HelloWorldImpl;
<b>uses</b>
JNI;
<b>procedure</b> Java_HelloWorld_displayHelloWorld(PEnv: PJNIEnv; Obj: JObject); <b>stdcall</b>;
<b>begin</b>
WriteLn(<font color="#9933CC">'Hello world!'</font>);
<b>end</b>;
<b>exports</b>
Java_HelloWorld_displayHelloWorld;
<b>end</b>.
</code></pre>
</BLOCKQUOTE>
<b>4. Execute the program.</b> You should open a command window (DOS prompt) in the directory where your Delphi and Java
files are and execute it there.
<BLOCKQUOTE>
<pre>
java Main
Hello world! <--- <i>this is displayed on the screen</i>
</pre>
</BLOCKQUOTE>
<p>
<a href="#Top">Back to top</a>
</BLOCKQUOTE>
<div class="SectionHeader">
<A NAME="CloserLook">A Closer Look at the <i>HelloWorld</i> Example</A>
</div>
<p>
<BLOCKQUOTE>
<b><A NAME=1>1. Create the Java Files</a></b>
<BLOCKQUOTE>
<i><A HREF="../Demos/Part1/HelloWorld.java">HelloWorld.java</a></i> Create the Java class that declares the native method. In this example, the
Java class definition is trivial. There are no members or methods defined within the class. There are 2 key points here:
<ul>
<li>Make sure to include the <b><tt>native</b></tt> keyword when
declaring the native method, otherwise, it's just another Java class method. This also tells the
JVM at runtime that the function is implemented in a DLL somewhere and not in
this class.
<li>Also, be sure that the name of the shared library is correct. Note that the
name of the DLL does not have to have the same name as the class that exposes the
method. In the example, the name of the class is <tt><b>HelloWorld</b></tt> and the
name of the DLL is <tt><b>HelloWorldImpl</b></tt>. (The <tt><b>static</b></tt> section of a class is executed when the
class is loaded. We use this section to load the DLL that contains the implementation of the native method.)
</ul>
<BLOCKQUOTE>
<pre class="sourcecode"><code><b>class</b> HelloWorld
{
<b>public</b> <b>native</b> <b>void</b> displayHelloWorld();
<b>static</b>
{
System.loadLibrary(<font color="#9933CC">"HelloWorldImpl"</font>);
}
}
</code></pre>
</BLOCKQUOTE>
Additional note: There is a tool that comes with the JDK called <tt>javah</tt>. This is a program that automatically generates
a C/C++ header file from the native methods in a class file. That's another reason why it is important to include the <b><tt>native</b></tt>
keyword. (Note to self: Write a javah-like tool that produces a Delphi-compatible file.)
<p>
<i><A HREF="../Demos/Part1/Main.java">Main.java</a></i> Create the Java class that will test the native method.
<ul>
<li> Notice that this Java code has no knowledge that the method <tt>displayHelloWorld</tt> is a native method. This allows the implementor
of <tt>HelloWorld.displayHelloWorld()</tt> to implement it either as a native method, or as a Java method. The calling program will never
know and will never need to be changed if the implementation of <tt>HelloWorld.displayHelloWorld()</tt> ever changes.
</ul>
<BLOCKQUOTE>
<pre class="sourcecode"><code><b>class</b> Main
{
<b>public</b> <b>static</b> <b>void</b> main(String[] args)
{
HelloWorld hw = <b>new</b> HelloWorld();
hw.displayHelloWorld();
}
}
</code></pre>
</BLOCKQUOTE>
</BLOCKQUOTE>
<b><A NAME=2>2. Compile the Java files.</a></b>
<BLOCKQUOTE>
<pre>
javac HelloWorld.java
javac Main.java
</pre>
This, of course, compiles the Java files and creates two files, <tt>HelloWorld.class</tt> and <tt>Main.class</tt>, respectively.
</BLOCKQUOTE>
<b><A NAME=3>3. Create and build the Delphi file.</b>
<BLOCKQUOTE>
Now things are getting a little interesting. There are several points here that you should take notice of. I'll go through this code in
much more detail than the Java code above. That's the main purpose of this document: I want to show you how to use Delphi to implement
native methods to be called from Java. I've also included line numbers below, so that I can easily refer to the source code.
<p>
First of all, notice that the file that implements the native method, <A HREF="src/HelloWorldImpl_dpr.html">HelloWorldImpl.dpr</a>,
is a <tt>.dpr</tt> file: a Delphi project file. Some Delphi programmers may never have spent much time dealing with this file. This
is quite natural, since the Delphi IDE does a pretty good job of generating and modifying this file for you automatically. The project
file is not that different from other units (<tt>.pas</tt> files.) I decided to put everything in the project file because the code to
implement the method was so trivial. (You could have put the function in a unit file and then added the unit to the <b>uses</b> section
in the project file.)
<p>
Ok, let's look at the file in detail:
<BLOCKQUOTE>
<pre class="sourcecode"><code>1. <b>library</b> HelloWorldImpl;
2.
3. <b>uses</b>
4. JNI;
5.
6. <b>procedure</b> Java_HelloWorld_displayHelloWorld(PEnv: PJNIEnv; Obj: JObject); <b>stdcall</b>;
7. <b>begin</b>
8. WriteLn(<font color="#9933CC">'Hello world!'</font>);
9. <b>end</b>;
10.
11. <b>exports</b>
12. Java_HelloWorld_displayHelloWorld;
13.
14. <b>end</b>.
</code></pre>
</BLOCKQUOTE>
<ul>
<li><b>Line 1:</b> Since this is a DLL, and not an executable program (<tt>.exe</tt>), we use the <b><tt>library</tt></b> keyword to
tell Delphi to create a DLL file. The name of the DLL will be <tt>HelloWorldImpl.dll</tt>.
<p>
<li><b>Line 4:</b> This is probably the most important line (if there can be only one.) This file, <a href="src/jni_pas.html">jni.pas</a>,
contains the interface to the JNI. This will be used by every DLL that needs to be accessed by Java. You can basically consider it
a black box. However, you have the source, so you can view it to get a better understanding of what's going on behind-the-scenes.
<p>
<li><b>Line 6:</b> There are several pieces of important information here that <i>must</i> be specified with each function that
is going to be called from Java:
<ol>
<p>
<li>This function is actually a Delphi <b><tt>procedure</tt></b>. This is simply because there is no return value. Had the Java class
declared the method to return a value, you would have seen the <b><tt>function</tt></b> keyword instead. We will see functions later on. Unless
otherwise specified, when I refer to a Delphi <b><tt>function</tt></b> I am also talking about a Delphi <b><tt>procedure</tt></b> as well.
<p>
<li>All functions/procedures that you want to be accessed from a Java class must have the term <b><tt>Java_</tt></b> prepended to them. Also,
the name of the class that declares the native method (<tt>HelloWorld</tt> in our example) must follow the <b><tt>Java_</tt></b> term. An underscore
character follows the Java class name and, finally, the name of the method itself. In our example, the
fully <i>decorated</i> function name is <b><tt>Java_HelloWorld_displayHelloWorld</tt></b>. Unlike Delphi, the Java language is case-sensitive,
so you must make sure that the Delphi function definitions use the same case as the Java method declarations. (Another reason for a tool
like <tt>javah</tt>. Maybe call it <tt>javad</tt>? Any takers?)
<p>
<li>What gives with the parameters? There were no parameters specified in the Java class! Every Java method that is implemented in native
code will have two additional parameters. They are both pointers that the native function can use to access functions in the Java object that called
the function and to access functions in the Java runtime environment. The types <tt>PJNIEnv</tt> and <tt>JObject</tt> are both defined in
<a href="src/jni_pas.html">jni.pas </a>
<p>
The first parameter is a pointer to the runtime environment. The second parameter
is a pointer to the object that called this method. The second parameter is sort of like the <b><tt>Self</tt></b> member of a class in Delphi. In this
example, since the function is so trivial, we don't use either of these. However, it is important that you include them in the definition of the
function because they will always be passed in to the function whether or not they are used. Later, we will see heavy use of these parameters.
<p>
<li>Unless you have written DLLs before or have had to access code written in another language (or implemented a Windows callback), you may have never
used the <b><tt>stdcall</tt></b> directive. This tells Delphi to generate code that will push the arguments onto the stack from right to left. Delphi
uses the fast <b><tt>register</tt></b> calling convention by default, which uses a combination of registers and the stack to pass arguments to functions.
It's not important to understand the details, but if you forget to add the
<b><tt>stdcall</tt></b> directive, your DLL will most surely cause an exception.
</ol>
<p>
<li><b>Line 8:</b> This is what we are actually trying to accomplish! It seems like a lot of work to print a line of text, and it is. However,
it's doubtful that anyone would go through this much trouble to print a line of text when they could clearly do it from Java. But, as you
probably have already figured out, in the real world we would be using/implementing some serious native code that just isn't possible or
efficient to implement in Java.
<p>
<li><b>Line 12:</b> In order for our function to be accessible from the outside, we must <i>export</i> it. This is done in
the <b><tt>exports</tt></b> section of a Delphi <b><tt>library</tt></b> (DLL.) Notice that you don't provide the parameters or
<b><tt>function/procedure</tt></b> keyword.
</BLOCKQUOTE>
</BLOCKQUOTE>
<b><A NAME=4>4. Execute the program.</b> In a nutshell, this is what's happening:
<BLOCKQUOTE>
<pre>
java Main <--- <i>you type this at the console</i>
</pre>
<ul>
<li>Java loads <i><tt>Main.class</tt></i> and calls <b><tt>main</tt></b>. This is typical for Java program.
<p>
<li>Java executes the line:
<pre>
HelloWorld hw = <b>new</b> HelloWorld();
</pre>
This line causes a new <tt>HelloWorld</tt> object to be created. When this class is created, the code inside it's <b><tt>static</tt></b>
section executes:
<pre class="sourcecode"><code> System.loadLibrary(<font color="#9933CC">"HelloWorldImpl"</font>);
</code></pre>
The <tt>System.loadLibrary</tt> method causes the DLL (<tt>HelloWorldImpl.dll</tt>) to be loaded by the operating system. If our DLL would have
wanted to perform any kind of startup action or initialization, it would have performed it at this time. (For this simple example there is no
initialization code.)
<p>
<li>Next, Java executes the line:
<pre>
hw.displayHelloWorld();
</pre>
This is a call to the <tt>displayHelloWorld</tt> method in the <tt>HelloWorld</tt> object. However, since this method is a <b><tt>native</tt></b> method
of the <tt>HelloWorld</tt> object, the call ends up going to the <tt>displayHelloWorld</tt> method (<tt>Java_HelloWorld_displayHelloWorld</tt>)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -