📄 appendixa.html
字号:
a pending
exception.</FONT><LI><FONT FACE="Symbol"> </FONT><FONT FACE="Georgia"><B>FatalError( )</B></FONT><BR><FONT FACE="Georgia">Raises
a fatal error. Does not
return.</FONT></UL><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Among these, you
can’t ignore <B>ExceptionOccurred( )</B> and
<B>ExceptionClear( )</B>. Most JNI functions can generate exceptions, and
there is no language feature that you can use in place of a Java try block, so
you must call <B>ExceptionOccurred( ) </B>after each JNI function call to
see if an exception was thrown. If you detect an exception, you may choose to
handle it (and possibly rethrow it). You must make certain, however, that the
exception is eventually cleared. This can be done in your function using
<B>ExceptionClear( )</B> or in some other function if the exception is
rethrown, but it must be done.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You must ensure that the exception
is cleared, because otherwise the results will be unpredictable if you call a
JNI function while an exception is pending. There are few JNI functions that are
safe to call during an exception; among these, of course, are all the exception
handling functions.</FONT><A NAME="_Toc408018823"></A><BR></P></DIV>
<A NAME="Heading593"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
JNI and threading</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Since Java is a multithreaded
language, several threads can call a native method concurrently. (The native
method might be suspended in the middle of its operation when a second thread
calls it.) It’s entirely up to the programmer to guarantee that the native
call is thread-safe, i.e. it does not modify shared data in an unmonitored way.
Basically, you have two options: declare the native method as
<B>synchronized</B> or implement some other strategy within the native method to
ensure correct, concurrent data manipulation.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Also, you should never pass the
<B>JNIEnv</B> pointer across threads, since the internal structure it points to
is allocated on a per-thread basis and contains information that makes sense
only in that particular thread.</FONT><A NAME="_Toc408018824"></A><BR></P></DIV>
<A NAME="Heading594"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Using a pre-existing code base</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The easiest way to implement JNI
native methods is to start writing native method prototypes in a Java class,
compile that class, and run the <B>.class</B> file through <B>javah</B>. But
what if you have a large, pre-existing code base that you want to call from
Java? Renaming all the functions in your DLLs to match the JNI name mangling
convention is not a viable solution. The best approach is to write a wrapper DLL
“outside” your original code base. The Java code calls functions in
this new DLL, which in turn calls your original DLL functions. This solution is
not just a work-around; in most cases you must do this anyway because you must
call JNI functions on the object references before you can use
them.</FONT><A NAME="_Toc408018825"></A><BR></P></DIV>
<A NAME="Heading595"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
The Microsoft way</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">At the time of this writing,
Microsoft does not support JNI, but provides proprietary support to call
non-Java code. This support is built into the compiler, the Microsoft JVM, and
external tools. The features described in this section will work only if your
program was compiled using the Microsoft Java compiler and run on the Microsoft
Java Virtual Machine. If you plan to distribute your application on the
Internet, or if your Intranet is built on different platforms, this can be a
serious issue.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The Microsoft interface to Win32
code provides three ways to connect to Win32: </FONT><BR></P></DIV>
<OL>
<LI><FONT FACE="Georgia"><B> J/Direct</B>: A way to easily call Win32 DLL
functions, with some limitations.</FONT><LI><FONT FACE="Georgia"><B> Raw
Native Interface (RNI)</B>: You can call Win32 DLL functions, but you must then
handle garbage collection.</FONT><LI><FONT FACE="Georgia"><B> Java/COM
integration</B>: You can expose or call COM services directly from
Java.</FONT></OL><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">I’ll cover
all three techniques in the following sections.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">At the time of writing, these
features were tested on the Microsoft SDK for Java 2.0 beta 2, which was
downloaded (with a painful process they call “Active Setup”) from
the Microsoft Web site. The Java SDK is a set of command-line tools, but the
compilation engine can be easily plugged into the Developer Studio environment,
allowing you to use Visual J++ 1.1 to compile Java 1.1
code.</FONT><A NAME="_Toc408018826"></A><BR></P></DIV>
<A NAME="Heading596"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
J/Direct</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">J/Direct is the simplest way to
call functions in a Win32 DLL. It was designed primarily to interface with the
Win32 API, but you can use it to call any other APIs. The ease of use of this
feature is counterbalanced by some limitations and reduced performance (compared
to RNI). But J/Direct has distinct advantages. First, there is no need to write
additional non-Java code, except the code in the DLL you want to call. In other
words, you do not need a wrapper or proxy/stub DLL. Second, function arguments
are automatically converted to and from standard data types. (If you must pass
user-defined data types, J/Direct might not be the way to go.) Third, it’s
simple and straightforward, as the example below shows. In just a few lines,
this example calls the Win32 API function <B>MessageBox( )</B>, which pops
up a little modal window with a title, a message, an optional icon, and a few
buttons.</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>public</font> <font color=#0000ff>class</font> ShowMsgBox {
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String args[])
<font color=#0000ff>throws</font> UnsatisfiedLinkError {
MessageBox(0,
<font color=#004488>"Created by the MessageBox() Win32 func"</font>,
<font color=#004488>"Thinking in Java"</font>, 0);
}
<font color=#009900>/** @dll.import("USER32") */</font>
<font color=#0000ff>private</font> <font color=#0000ff>static</font> <font color=#0000ff>native</font> <font color=#0000ff>int</font>
MessageBox(<font color=#0000ff>int</font> hwndOwner, String text,
String title, <font color=#0000ff>int</font> fuStyle);
}</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Amazingly, this code is all you
need to call a function in a Win32 DLL using J/Direct. The key is the
<B>@dll.import</B> directive before the <B>MessageBox( )</B> declaration,
at the bottom of the example code. It looks like a comment, but it’s not:
it tells the compiler that the function below the directive is implemented in
the USER32 DLL, and should be called accordingly. All you must do is supply a
prototype that matches the function implementation in the DLL and call the
function. But instead of typing in the Java version of each Win32 API function
that you need, a Microsoft Java package does this for you (I’ll describe
this shortly). For this example to work, the function must be exported <I>by
name</I> by the DLL, but the <B>@dll.import </B>directive can be used to link
<I>by ordinal</I> as well, i.e., you can specify the entry position of the
function in the DLL. I’ll cover the features of the <B>@dll.import
</B>directive later.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">An important issue in the process
of linking with non-Java code is the automatic marshaling of the function
parameters. As you can see, the Java declaration of <B>MessageBox( )</B>
takes two String arguments, but the original C implementation takes two
<B>char</B> pointers. The compiler automatically converts the standard data
types for you, following the rules described in a later
section.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Finally, you might have noticed the
<B>UnsatisfiedLinkError</B> exception in the declaration of <B>main( )</B>.
This exception occurs when the linker is unable to resolve the symbol for the
non-Java function at run-time. This happens for a number of reasons: the
<B>.dll</B> file was not found, it is not a valid DLL, or J/Direct is not
supported by your virtual machine. For the DLL to be found, it must be in the
Windows or Windows\System directory, in one of the directories listed in your
PATH environment variable, or in the directory where the <B>.class </B>file is
located. J/Direct is supported in the Microsoft Java compiler version 1.02.4213
or above, and in the Microsoft JVM version 4.79.2164 or above. To get the
compiler version number, run JVC from the command line with no parameters. To
get the JVM version number, locate the icon for <B>msjava.dll</B>, and using the
context menu look at its
properties.</FONT><A NAME="_Toc408018827"></A><BR></P></DIV>
<A NAME="Heading597"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
The @dll.import directive<BR><A NAME="Index3087"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>@dll.import</B> directive,
your one and only way to J/Direct, is quite flexible. It has a number of
modifiers that you can use to customize the way you link to the non-Java code.
It can also be applied to some methods within a class or to a whole class,
meaning that all of the methods you declare in that class are implemented in the
same DLL. Let’s look at these features.</FONT><BR></P></DIV>
<A NAME="Heading598"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
Aliasing and linking by ordinal</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">For the <B>@dll.import</B>
directive to work as shown above, the function in the DLL must be exported by
name. However, you might want to use a different name than the original one in
the DLL (aliasing), or the function might be exported by number (i.e. by
ordinal) instead of by name. The example below declares
<B>FinestraDiMessaggio( )</B> (the Italian equivalent of
“MessageBox”) as an alias to <B>MessageBox( )</B>. As you can
see, the syntax is pretty simple.</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>public</font> <font color=#0000ff>class</font> Aliasing {
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String args[])
<font color=#0000ff>throws</font> UnsatisfiedLinkError {
FinestraDiMessaggio(0,
<font color=#004488>"Created by the MessageBox() Win32 func"</font>,
<font color=#004488>"Thinking in Java"</font>, 0);
}
<font color=#009900>/** @dll.import("USER32",
entrypoint="MessageBox") */</font>
<font color=#0000ff>private</font> <font color=#0000ff>static</font> <font color=#0000ff>native</font> <font color=#0000ff>int</font>
FinestraDiMessaggio(<font color=#0000ff>int</font> hwndOwner, String text,
String title, <font color=#0000ff>int</font> fuStyle);
}</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The next example shows how to link
to a function in a DLL that is not exported by name, but by its position inside
of the DLL. The example assumes that there is a DLL named MYMATH somewhere along
your path, and that this DLL contains at position 3 a function that takes two
integers and gives you back the sum.</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>public</font> <font color=#0000ff>class</font> ByOrdinal {
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String args[])
<font color=#0000ff>throws</font> UnsatisfiedLinkError {
<font color=#0000ff>int</font> j=3, k=9;
System.out.println(<font color=#004488>"Result of DLL function:"</font>
+ Add(j,k));
}
<font color=#009900>/** @dll.import("MYMATH", entrypoint = "#3") */</font>
<font color=#0000ff>private</font> <font color=#0000ff>static</font> <font color=#0000ff>native</font> <font color=#0000ff>int</font> Add(<font color=#0000ff>int</font> op1,<font color=#0000ff>int</font> op2);
}</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see the only difference is
the form of the <B>entrypoint</B> argument.</FONT><BR></P></DIV>
<A NAME="Heading599"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
Applying @dll.import to the entire class</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The
<A NAME="Index3088"></A><B>@dll.import</B> directive can be applied to an entire
class, meaning that all of the methods in that class are implemented in the same
DLL and with the same linkage attributes. The directive is not inherited by
subclasses; for this reason, and since functions in a DLL are by nature
<B>static</B> functions, a better design approach is to encapsulate the API
functions in a separate class, as shown here:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>/** @dll.import("USER32") */</font>
<font color=#0000ff>class</font> MyUser32Access {
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>native</font> <font color=#0000ff>int</font>
MessageBox(<font color=#0000ff>int</font> hwndOwner, String text,
String title, <font color=#0000ff>int</font> fuStyle);
<font color=#0000ff>public</font> <font color=#0000ff>native</font> <font color=#0000ff>static</font> <font color=#0000ff>boolean</font>
MessageBeep(<font color=#0000ff>int</font> uType);
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> WholeClass {
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String args[])
<font color=#0000ff>throws</font> UnsatisfiedLinkError {
MyUser32Access.MessageBeep(4);
MyUser32Access.MessageBox(0,
<font color=#004488>"Created by the MessageBox() Win32 func"</font>,
<font color=#004488>"Thinking in Java"</font>, 0);
}
}</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Since the
<B>MessageBeep( )</B> and <B>MessageBox( )</B> functions are now
declared as static in a different class, you must call them specifying their
scope. You might think that you must use the approach above to map <I>all </I>of
the <A NAME="Index3089"></A>Win32 API (functions, constants, and data types) to
Java classes. Fortunately, you don’t have
to.</FONT><A NAME="_Toc408018828"></A><BR></P></DIV>
<A NAME="Heading600"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
The com.ms.win32 package</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The Win32 API is fairly big –
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -