To install ProxyGen, simply unzip the downloaded file and ProxyGen.exe is ready
to run.
To deploy the generated .NET proxy assembly DLL, just make sure you have JInt.DLL in
the same directory. Please see Using Generated Proxy Assembly for more detail.
ProxyGen is a product that enables the .NET platform to directly call Java code.
There are many ways to implement .NET-Java interoperability, for example, you can
use Web Service, or you can use .NET remoting. ProxyGen uses JNI (Java Native Interface) to
directly access Java object. Performance wise, JNI easily beats the other approaches,
because both the .NET and Java objects run inside the same WIN32 process.
ProxyGen consists of two components, ProxyGen.exe and JInt.DLL. ProxyGen.exe
is a WIN32 GUI application that generates .NET proxy assembly DLL for Java types; JInt.DLL is a WIN32 DLL
that is called by the generated .NET proxy to access Java VM. ProxyGen.exe is the
development tool and JInt.DLL is the runtime deployment unit.
How it works. In the first step, you use ProxyGen.exe to generate a .NET assembly DLL that contains
proxy classes for the corresponding Java classes. These proxy classes are .NET classes that have
the same public fields/methods as their Java Classes. ProxyGen.exe reads Java class meta data directly from .jar or .class files,
so you don't need to have the Java source code, you don't even need JRE, since ProxyGen.exe does not rely on
the Java Reflection APIs. You do need to have the .NET runtime installed, ProxyGen.exe uses the C# compiler to generate
the .NET proxy assembly.
In the next step, in your .NET development environment, add the generated proxy assembly DLL as a Reference
for your .NET project; your .NET code can then call the proxy classes as if they were the original Java classes,
because they have the same public fields/methods. Actually, to be more precise, Java fields are mapped to .NET properties.
In the final step, you deploy your application with the generated proxy assembly and JInt.DLL.
When the proxy class' field or method is accessed, it uses .NET's Platform Invoke to call the WIN32 DLL JInt.DLL with
the necessary parameters. JInt.DLL then uses JNI to load the Java VM (Virtual Machine) and
accesses the underlying java object. The return value, if any, is passed back to
the proxy class and to the caller. On your deploy target machine, you need to have JRE installed, you also need to have
all the necessary .jar or .class files. See Figure 1.
Figure 1. How It Works
To load Java VM, JInt.DLL examines the registry key \\LocalMachine\Software\JavaSoft\Java Runtime Environment,
looks for the latest version key, and reads the "RuntimeLib" value to get the complete path of the Java VM DLL.
The only way to get JInt.DLL to load your customized Java VM is to manually modify the registry key value.
Figure 2. depicts a typical ProxyGen.exe UI layout.
Figure 2. ProxyGen.exe UI Layout
The left pane window contains a tree view control. Loaded .jar files will appear as
the top level nodes. Java packages that are contained in a .jar file will appear as child
nodes under the .jar file's node, and classes in the a package will appear as child
nodes under the package's node. If a class has nested classes, they will appear as child
nodes of the parent class node. You can also directly load a .class file, the node for the
class will appear as a top level node of the tree view control.
Each node of the tree view has an associated icon. There are 5 different icons: jar file,
package, class, interface, and exception. If the class/interface/exception is not public, the icon
image is grayed.
Each node also has an associated check box. You click on it to check or uncheck the node. Checking or
unchecking a parent node will check or uncheck all its child nodes. When you build the
proxy assembly, only checked public classes will be included.
The right pane window displays the Java class definition that is highlighted on the left pane.
It shows the class members, base class and implemented interface, if any. Non-public members are displayed in gray color.
When the mouse cursor moves over a Java type (such as base class, implemented interface, or method parameters), the cursor
changes to a hand. You can click on the Java type to display class definition of that type, if the
type is loaded. Also, when the mouse cursor is over the base class,
the class methods in the current class that override the base class methods are yellow-color
highlighted. When the mouse cursor is over an implemented interface, the class
methods that implement the interface are yellow-color highlighted.
If your machine has JRE installed, you can use the File|Open|Java Runtime menu command or
the equivalent tool bar button to load the Java Runtime, rt.jar. ProxyGen.exe will
automatically locate rt.jar using registry key value; other than this, rt.jar is treated the same as any other
.jar file or .class file. For example, on a machine that has no JRE installation, you can copy rt.jar from a JRE-installed
machine, and use the File|Open...|Jar/Class to open it and generate a proxy assembly DLL for the entire Java Runtime.
Once a .jar or .class file is opened, a node is added to the tree view control, the label is the complete path
of the .jar or .class file. In the case of .jar file, all the packages inside the .jar file will appear as child
nodes; and all the Java types inside a package will appear as child nodes of the package node. You use the associated
check box to select/unselect a Java type. Selecting/Unselecting a parent node will select/unselect all its child nodes.
You can't open the same .jar or .class file more than once. However, you can copy the same .jar file
to another location and open it. It is possible that you have two copies of a particular class loaded, contained
in two different .jar files. When proxy is generated, the first copy will be used.
To close a loaded .jar or .class file, highlight it's node on the tree view control, and use the menu command
File|Close|Jar/Class. You can re-load the same file after you close it.
To view your selection summary, you use the menu command View|Selection, or the equivalent tool bar button.
The "Summary" dialog box will appear. See Figure 3. Depending on how many Java types you select, this could be a long process,
because ProxyGen.exe needs to scan for support types and check methods. You can cancel the process by
clicking on the flashing tool bar button.
Figure 3. Summary Dialog Box
The first list box contains all the Java types you selected. Although you can select a non-public Java type,
proxy will not be generated for it. It's name is prefixed by the ~ character inside the list box.
If a type's name is prefixed by the ! character, it indicates that one or more of the type's member is changed or hidden to
avoid naming conflict. Please see Class Member Naming Conflict : Prefix and Hidding for more details.
The second list box contains all the support Java types. A support type is a Java class or interface that is not directly selected by the user, but is used by at least one selected type. The support type of a selected type includes:
For example, given the following Java class MyJavaClass:
import pk.YourJavaClass; import pk.Int1; import pk.Int2; import pk.FriendClass; import pk.ThirdPartyClass; import pk.PException; public class MyJavaClass extends YourJavaClass implements Int1, Int2 { public FriendClass _friendClass; public void process(ThirdPartyClass target) throws PException {...} }
The proxy class for a support type does not include any member. For example, the generated proxy for pk.YourJavaClass looks like this (C# code):
namespace pk { public class YourJavaClass : java.lang.Object { } }
namespace pk { public class YourJavaClass : pk.HisJavaClass { } }
You don't need to load a support type in order to generate the proxy assembly. An '*' is prefixed to
its name in the Support Type list box. In that case,
the support type's base type is unknown, we will use java.lang.Object if the type is a class, java.lang.Throwable
if the type is an exception. For interface type, we use an internal interface for base.
If a selected Java class implements an interface, and some of the interface methods are implemented in the base class which is not loaded, ProxyGen.exe will add these methods to the selected Java class.
You can specify information about your .NET proxy assembly using the "Assembly Information"
dialog box, as shown in Figure 4. You display this dialog box by the menu command Configure|Assembly Info, or the
equivalent tool bar button.
Figure 4. Assembly Information Dialog Box
Of all the fields, only "DLL File" is mandatory. If you have not specified the target DLL name and do a build,
ProxyGen.exe will prompt you to enter the target DLL file name. In order to generate a strong-named proxy assembly,
you either specify the "Key File" or the "Key Name" field.
The "Configure Options" dialog box allows you to specify Build Options and default Java VM Options. You
can access this dialog box by using the menu command Configure|Options, or the equivalent
tool bar button. The "Convert array to .NET native" option instruct ProxyGen.exe to convert Java array to .NET array.
For example, the following Java class: In some cases, this can cause problem. In the following Java code, the class MyJavaIntArrayExample is used:
The call calc() will return 0. On the other hand, if you compile the same class UseIntArray
in C# with the MyJavaIntArrayExample poxy, calc() will return 200. This is because
the call jia.getTheArray() returns a copy of the Java integer array, the line: Notice the getTheArray() call returns an instance of the class intJArray. This class is automatically
generated by ProxyGen.exe, it is a proxy class for the underlying Java integer array, and it behaves like
a native .NET interger array. With this, the above examples becomes: You can see that everything remains the same, except that "int[] theArray" becomes
"intJArray theArray". And this time, calc() will return 0. If you happen to have a Java class named intJArray, ProxyGen.exe will
append "CLS" to intJArray as the array proxy name. It will keep appending "CLS" until
there is no more name conflict. The proxy class for int[][] by default is intJArray2D,
the default for int[][][] is intJArray3D, and so on. The same rule applies for other types.
For example, double[] is doubleJArray; double[][] is doubleJArray2D, etc. These array classes are generated as needed. For example, if in all the selected Java classes,
there's no public field/method whose signature refers to int array of any dimension, then intJArray will not be
generated. The "Java VM Option" section allows you to set the default Java VM loading option for runtime environment.
If you check "Use Default", at runtime, Java VM will be loaded with maximum memory 128M, and the Java class path
will be set to the value of the environment variable "Classpath". If you don't check this
option, you can set the maximum memory and class path individually. The "Fill" button next to
the "Java classpath" edit box will fill the edit box with the paths of the currently loaded .jar/.class files.
Please note, these .jar or .class files may be in different locations on your deploy target machine,
in that case, you can't use the "Fill" button, you need to specify the paths to reflect the deploy machine. The VM options
are used during runtime, ProxyGen.exe does not load Java VM. You also have the option to change these setting and more during runtime. ProxyGen.exe generates a
class called ___BinaryInterop.BInteropConfig, its only member is a public string array jvmOptions:
You have to set the options before any of the proxy class is used. The number of underscores (_) prefixed to the
namespace BinaryInterop may be more than 3 to avoid any naming conflict with selected Java types. Setting
___BinaryInterop.BInteropConfig.jvmOptions to null will cause the Java VM to be loaded with the "Use Default" option. The View|Back,Forward allows you to navigate back/forward to Java class definition in the right pane window.
View|Sync highlights the corresponding tree view control node for the currently displayed Java class in the right pane.
View|Font allows you to change the font used in Java class definition pane. Once you are done with selecting Java types and are OK with the various options, you can use the File|Build menu command
or the equivalent tool bar button to build the proxy. If you have not specified your target .NET proxy DLL file
name, the program will prompt you to enter a file name. Building proxy can be a very lengthy process, depending on
how many Java types you selected. While the build process is going on, the "Build" tool bar button flashes, you can cancel
the process by clicking on the flashing tool bar button. The build process is cancelled after confirmation.
Once you generated the proxy assembly, you add it as a Reference to your .NET development project. You can then call the proxy classes
as if they are the original Java classes, because they have the idential public members. You deploy your .NET project together with the proxy assembly and the WIN32 native DLL JInt.DLL. Your target machine must have J2SE 1.4 or above installed.
You can have more than one versions of JRE installed, JInt.DLL will always load the latest version. JInt locates the Java VM DLL by examining the registry
key "Software\JavaSoft\Java Runtime Environment"; it pickes the latest version under this registry key, and read the value "RuntimeLib" to get
the complete path of the Java VM. For example, under the key "Software\JavaSoft\Java Runtime Environment" you have two sub keys "1.4" and "1.5", JInt.DLL will
read the value "RuntimeLib" from the sub key "1.5". You need all the .jar file or .class files required to load and run the selected Java types.
You are also responsible to setup the correct java class path so that the Java VM can load them. There are 3 ways you can specify class path : 1) During proxy assembly generation, you can supply the Java class path;
2) You can use the environment variable "classpath" on the deployment machine; 3)You can control the class path from your .NET code.
Please see Java VM Option in Options for more detail. An easy way is to
check the "Use Default" option in the "Configure Options" dialog box, and setup the environment variable "classpath" correctly on
you target machine. Primitive types are mapped according to the following table:
Figure 5. Configure Options Dialog Box
Build Options.
If you check "Produce html document", a html document will be generated to list all the proxy classes, their
public members. The html file name will be the same as the assembly DLL name, except for the file extension.
For example, if the assembly file name is c:\mydir\myproxy.dll, the html file name would be
c:\mydir\myproxy.htm. This option is un-checked by default.
public class MyJavaIntArrayExample
{
private int[] _theArray = {100, 100};
public int[] getTheArray() {return _theArray;}
}
With the option checked, the .NET proxy class looks like (C# code):
public class MyJavaIntArrayExample
{
public int[] getTheArray() {...}
}
This is what most people expect. The Java method returns a Java integer array (which is an object
in Java environment), the proxy code creates a .NET integer array, copies the values from the Java
array, and returns the .NET integer array to the caller. Once the Java integer array is copied to
a .NET integer array, it no longer has any relationship with the .NET array. That is, if
you make any change to the .NET integer array, the change will not go back to the Java array.
public class UseIntArray
{
public void initIntToZero(MyJavaIntArrayExample jia)
{
int[] theArray = jia.getTheArray();
theArray[0] = theArray[1] = 0;
}
public int getSum(MyJavaIntArrayExample jia)
{
int[] theArray = jia.getTheArray();
return theArray[0] + theArray[1];
}
public int calc()
{
MyJavaIntArrayExample jia = new MyJavaIntArrayExample();
initIntToZero(jia);
return getSum(jia);
}
}
theArray[0] = theArray[1] = 0;
does not modify the Java integer array. So in getSum(), we get a copy of the Java integer array,
which remains to be {100, 100}.
If you have this type of logic in the Java class, you need to turn off the "Convert array to .NET native"
option. The generated proxy for MyJavaIntArrayExample looks like (C# code):
public class MyJavaIntArrayExample
{
public intJArray getTheArray() {...}
}
public class UseIntArray
{
public void initIntToZero(MyJavaIntArrayExample jia)
{
intJArray theArray = jia.getTheArray();
theArray[0] = theArray[1] = 0;
}
public int getSum(MyJavaIntArrayExample jia)
{
intJArray theArray = jia.getTheArray();
return theArray[0] + theArray[1];
}
public int calc()
{
MyJavaIntArrayExample jia = new MyJavaIntArrayExample();
initIntToZero(jia);
return getSum(jia);
}
}
namespace ___BinaryInterop
{
public class BInteropConfig
{
public string[] jvmOptions = null;
}
}
If you want to control the Java VM loading option in a more refined way, you can set jvmOptions to any legal
VM options. For example, to set the maximum memory to 256M, and class path to c:\myjavaclasspath, you call:
// do this before any of the proxy class is used
...
string[] myjavaoptions = {
@"-Djava.class.path=c:\myjavaclasspath",
@"-Xmx256m"
};
___BinaryInterop.BInteropConfig.jvmOptions = myjavaoptions;
...
View Menu
Build Proxy
Project
A project is a snap shot of your current work status. It includes the .jar/.class files you've loaded, the Java types you've
selected and the various options you modified. You can save all this to a projet file and continue the work later.
Using Generated Proxy Assembly
Mapping Java Type to .NET
Java Type | .NET Type |
---|---|
byte | sbyte |
short | short |
int | int |
long | long |
float | float |
double | double |
char | char |
boolean | bool |
java.lang.String is mapped to .NET string.
Java package name is mapped to .NET namespace; Java class is mapped to .NET class with the same name; Java
interface is mapped to .NET interface with the same name. For example, java.lang.Object is mapped to (C# code) :
namespace java.lang { public class Object { ..... } }The Java interface java.lang.CharSequence is mapped to .NET interface (C# code):
namespace java.lang { public interface CharSequence { ..... } }
Java class public methods are mapped to the .NET proxy class public methods with the same signature; Java public fields
are mapped to .NET proxy class properties with the same type.
Nested Java type is mapped to nested .NET type, with one exception regarding nested type
inside interface. For example, the following nested in Java:
package java.lang; public class Thread { public class State { .... } ... }
namespace java.lang { public class Thread { public class State { .... } ... } }
However, the following nested type within a Java interface:
package javax.swing.text; public interface AttributeSet { public interface CharacterAttribute { .... } public interface ColorAttribute { .... } public interface FontAttribute { .... } ... }
namespace javax.swing.text { public interface AttributeSet { ... } public interface AttributeSet_CharacterAttribute { .... } public interface AttributeSet_ColorAttribute { .... } public interface AttributeSet_FontAttribute { .... } }
Nested type within Java interface will not be nested in .NET; instead, the name of the
nested type will be the parent type combined with the child type, separated with an underscore (_).
In some cases, in order to avoid naming conflict, more than one underscores may be used.
This is done because ProxyGen.exe uses C# compiler to generate the .NET proxy assembly, and C# does not
allow any interface to have nested type. Although the underlying .NET platform does allow an interface
to have nested type, the C# language will have a hard time using it, so we decided to "flatten" out the nested
type inside interface.
C# does not allow interface to have any constant, while in Java, you can, for example:
package javax.swing.text; public interface AttributeSet { public static final java.lang.Object NameAttribute = ....; public static final java.lang.Object ResolveAttribute = ...; .... }
When this interface is mapped to .NET, besides the .NET interface javax.swing.text.AttributeSet,
another .NET class is generated to hold the constants:
namespace javax.swing.text { public class AttributeSetConst { public static readonly java.lang.Object NameAttribute = ...; public static readonly java.lang.Object ResolveAttribute = ...; } }
Please note, in some cases, in order to avoid naming conflict, additional suffix "INT" may be added
to the class name.
In a Java class, you can have a method with the same name as the class; and a field with the same name
as a method. C# does not allow these. In the generated proxy class, if a field name conflicts with class name or method name,
it is prefixed with one underscore (_); if a method name conflicts with class name, it is prefixed with
two underscores (__). The number of underscores may increase, if any original Java member name starts with underscore.
Starting from Java 5.0, you can change the return type of a inherited or interface method, if the new return type can be casted to the
original return type. Consider the following Java types from J2SE 5.0 runtime rt.jar (additional member ignored for simplicity):
package java.lang; public interface Appendable { public Appendable append(char c); } .... package java.io; public abstract class Writer implements java.lang.Appendable { public java.io.Writer append(char c) { .... } } public CharArrayWriter extends java.io.Writer { public java.io.CharArrayWriter append(char c) { ... } }
This code will not compile before J2SE 5.0, because the return type for the method append() is changed.
However, it makes perfect sense to allow this, because, an instance of java.io.CharArrayWriter
can be casted to an instance of java.io.Writer, which in turn can be casted to java.lang.Appendable.
This code compiles with J2SE 5.0. If you examine the generated class file, the compiler adds methods to
java.io.Writer and java.io.CharArrayWriter:
package java.io; public abstract class Writer implements java.lang.Appendable { public java.io.Writer append(char c) { .... } public java.lang.Appendable append(char c) // added by compiler { ... } } public CharArrayWriter extends java.io.Writer { public java.io.CharArrayWriter append(char c) { ... } public java.io.Writer append(char c) // added by compiler { ... } public java.lang.Appendable append(char c) // added by compiler { ... } }
C# does not allow the change of return type. In the .NET proxy types, only the method
java.lang.Appendable(char c) will be present, the other two versions, with the return types
java.io.CharArrayWriter and java.io.Writer will not be present.
If you choose to generate a html document for the .NET proxy types, all the types whose members
are changed/hidden will be colored red.
Any exception in the Java VM runtime environment is passed back to .NET. If you remember, exceptions thrown
by public methods of select Java classes are automatically included as support classes. If you need to access
public members of these exception classes, you need to explicitly select them for proxy generation.
When the Java VM can't load a certain Java type, it throws the error java.lang.NoClassDefFoundError; since the Java class loading
action is initiated in the .NET proxy's static constructor, .NET wrapps it inside an instance of System.TypeInitializationException,
with the InnerException pointing to java.lang.NoClassDefFoundError. If you have selected java.lang.Throwable and
java.lang.NoClassDefFoundError, for proxy generation, you can access their members to find out which class can't be loaded.