home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 14 / IOPROG_14.ISO / soft / sdkjava / sdkjava.exe / SDKJava.cab / Samples / CustDoc / CustomMarshal.txt next >
Encoding:
Text File  |  1998-03-05  |  34.5 KB  |  878 lines

  1. ===========================================================================
  2.        - JAVA/COM CUSTOM MARSHALING FOR USER-DEFINED DATA TYPES -
  3.  
  4.      Copyright (c) 1998 Microsoft Corporation.  All rights reserved.
  5. ===========================================================================
  6.  
  7. This document explains how custom marshaling can be implemented between Java
  8. and COM.  Basically, this involves the writing of a HOOK CLASS to do the
  9. marshaling, and the use of the JACTIVEX tool to create a Java type library
  10. file that describes the custom interface. 
  11.  
  12. Suppose you have a COM library that uses various data types as parameters 
  13. and return values for its methods.  For these methods to be callable from
  14. Java, the data types need to be converted between the COM types and Java
  15. types.  For some types, like integers, floats, booleans, and strings, the
  16. Microsoft VM for Java provides intrinsic marshaling support that does the
  17. type conversion for you.
  18.  
  19. Besides the conversion work, the COM types must be represented as Java
  20. types in order to be callable from Java.  This requires a Java class
  21. file that contains type declarations in Java format.  This class file is
  22. usually created by running a type-conversion tool such as JACTIVEX (with
  23. the /javatlb switch) on the typelib of the COM server.  This tool will
  24. generate the proper type mappings for all types that are supported by
  25. the intrinsic marshaling of the VM. 
  26.  
  27. However, the VM does not have intrinsic support for complex COM library
  28. data types such as C structures.  In such cases, you will need to create
  29. a user-defined Java type and write a HOOK CLASS that does the marshaling.
  30.  
  31. Also, JACTIVEX cannot automatically create type mappings that will work for
  32. user-defined types.  You will need to tell JACTIVEX how to generate the Java
  33. type declarations.  You do this by creating a Java Information (.JNF) file
  34. that describes your custom data types, and passing this extra file to JACTIVEX
  35. when it creates the COM library class header file. 
  36.  
  37. The remainder of this document provides information on how this is done.
  38.  
  39.  
  40.  
  41. CUSTOM MARSHALING OVERVIEW
  42. ==========================
  43. Suppose you have a simple COM library, represented by this pseudo-ODL file:
  44.  
  45.             typedef struct POINT
  46.             {   long    x;
  47.                 long    y;
  48.             } POINT;
  49.  
  50.             interface IPlotter : IUnknown
  51.             {
  52.                 HRESULT DrawLine([in] POINT *start, [in] POINT *end);
  53.             }
  54.         
  55.             coclass CPlotter
  56.             {
  57.                 [default] interface IPlotter;
  58.             }
  59.  
  60. To implement or access this library with Java, you would go through
  61. the following steps:
  62.  
  63.     1. Write a PLOTTER.IDL or PLOTTER.ODL file for the COM library.
  64.        Pass it to MIDL or MKTYPLIB to generate a type library called
  65.        PLOTTER.TLB.
  66.  
  67.  
  68.     2. Write a .JNF file which describes all the user-defined types.
  69.        Here is what the .JNF file would look like for the above
  70.        POINT type:
  71.  
  72.        PLOTTER.JNF
  73.        -----------
  74.        [Custom]
  75.        POINT = java.awt.Point, PointMarshaler
  76.  
  77.        This declaration tells JACTIVEX that when it sees a reference
  78.        to the type named POINT in the typelib, it should substitute the
  79.        Java type java.awt.Point.  It also tells the VM  to use the
  80.        PointMarshaler.class as the HOOK CLASS for the POINT type.
  81.        A hook class is a special class that you write for each
  82.        user-defined type you create.  It contains the code that actually
  83.        does the marshaling.  In other words, it translates the COM type
  84.        to the Java type and vice-versa.  Much of this document will deal
  85.        with what a hook class looks like and how to author one.
  86.  
  87.        IMPORTANT: IDL and ODL files differ on how they expose the type
  88.        name.  If your typedef is:
  89.  
  90.        typedef struct _Foo
  91.        {
  92.          ...
  93.        } FOO;
  94.  
  95.        you need to put the struct tag name ("_Foo") in the JNF if
  96.        you are writing an IDL file.  If you are writing an ODL file,
  97.        you must put the typedef name ("FOO").
  98.  
  99.  
  100.     3. Pass the TLB and the JNF file to JACTIVEX.  It will create two
  101.        .java files that include special syntax.  This syntax will generate
  102.        the extra bits the Microsoft VM for Java needs to treat these files
  103.        as Java/COM classes.  For example, the name of the "PointMarshaler"
  104.        class is part of these bits.
  105.  
  106.        For this example, JACTIVEX will generate two files: CPlotter.java
  107.        (representing the coclass), and IPlotter.java (representing the
  108.        interface).  Excluding the extra syntax for the magic bits, the
  109.        generated source for IPlotter looks like this:
  110.  
  111.        IPLOTTER.JAVA
  112.        -------------
  113.        // In the actual file, you will see extra syntax that
  114.        // represents the extra bits needed for the VM Java/COM
  115.        // marshaling layer.
  116.        
  117.        interface IPlotter
  118.        {
  119.            public DrawLine(java.awt.Point start, java.awt.Point end);
  120.        }
  121.  
  122.  
  123.     4. Compile CPlotter.java and IPlotter.java using JVC.  You must use
  124.        JVC version 1.02.3920 or later, which understands the extra syntax
  125.        that JACTIVEX generates.  This version of JVC is included in the BIN
  126.        directory of the SDK.
  127.  
  128.  
  129.     5. A Java application can now load and use an installed Plotter
  130.        library by invoking:
  131.  
  132.           IPlotter plotter = (IPlotter)(new CPlotter());
  133.           plotter.DrawLine(new Point(x1,y1), new Point(x2,y2));
  134.  
  135.        A Java class can also implement the Plotter library via IPlotter:
  136.        
  137.           class MyPlotterImp implements IPlotter
  138.           {
  139.              void DrawLine(Point start, Point end)
  140.              {
  141.                ...
  142.              }
  143.           }
  144.  
  145. The sources for a working copy of this example can be found under the
  146. plotter subdirectory.  The sources for PointMarshaler are under the point
  147. subdirectory.  To execute the sample, change the current directory to
  148. the prundir subdirectory, and type "GO".
  149.  
  150.  
  151.  
  152. HOW JAVA/COM CUSTOM MARSHALING WORKS
  153. ====================================
  154. How does the Microsoft VM for Java actually marshal the call from Java to
  155. Plotter?  In the interface file IPlotter.class, there are special bits
  156. associated with the "start" and "end" parameters that indicate that these
  157. parameters are a user-defined class and that the hook class is
  158. PointMarshaler.class.  These bits are all generated by JACTIVEX.EXE.
  159.  
  160. When a Java application invokes the DrawLine() method, the Microsoft VM for
  161. Java loads the PointMarshaler class (if it hasn't already), and looks for a
  162. public static field named "cbByValSize", whose presence indicates that the
  163. COM type is fixed-size.  In this example, PointMarshaler does expose this
  164. field, and it is equal to 8, the size (in bytes) of a POINT structure.
  165.  
  166. Hence, the Microsoft VM for Java allocates 8 bytes of space on the stack,
  167. and invokes another public static method on PointMarshaler called
  168. copyToExternal.  The VM passes the copyToExternal method a pointer to
  169. this 8-byte space, and a reference to the "end" parameter, in this
  170. case java.awt.Point.  (NOTE: the pointer to the 8-byte space is passed
  171. as an integer value).
  172.  
  173. With this information, copyToExternal translates the java.awt.Point to
  174. a POINT structure in that 8 byte space. The same is repeated for the
  175. "end" argument.  The COM method DrawLine ultimately receives a pointer
  176. to the two stack buffers as its "start" and "end" parameters.  Since both
  177. parameters were marked "[in]",  there is no further action for this simple
  178. type.
  179.  
  180. NOTE: The above explanation is necessarily simplified.  Much of the hook
  181. class analysis implied above is done prior to any method calls and does
  182. not actually occur at method call time.
  183.  
  184.  
  185.  
  186. HOOK CLASS OVERVIEW
  187. ===================
  188. Aside from the extra step of writing a hook class, and listing your data
  189. types in the JNF file, the procedure above is no different from that of
  190. integrating any other COM library with Java.  The hook class is the
  191. cornerstone of user-defined types.  Therefore, this document is primarily
  192. about writing hook classes.
  193.  
  194. A hook class is a collection of static methods and fields packaged
  195. as a Java class.  The hook class must be installed and visible to the
  196. classpath on any system where the user-defined types are used.  However,
  197. they otherwise remain "behind the scenes."  The Microsoft VM for Java loads
  198. the hook class as part of loading a JACTIVEX-created interface file that
  199. references them, and invokes methods on the hook class in order to marshal
  200. the user-defined type.
  201.  
  202. All hook methods and fields are static (i.e. per-class, rather than
  203. per-instance).  The Java/COM integration layer of the VM will never create
  204. an instance of a hook class.  There are approximately 10 fields and methods
  205. defined by the Hook Class Specification; however, all hook members are
  206. either optional or have reasonable defaults, so most hook classes will
  207. only need to implement a subset of the members.
  208.  
  209. The responsibilities of a hook class are:
  210.  
  211.   1. Specifying the Java type (which we will refer to generically as JTYPE).
  212.      JTYPE can be a primitive type (int, double, etc.) or a reference
  213.      type (objects and arrays).
  214.  
  215.   2. Specifying the size, in bytes, of the COM type (which we will refer
  216.      to generically as ETYPE).  ETYPE can be variable-sized, in which
  217.      case, the hook class will indicate this fact by not offering a size.
  218.  
  219.   3. Providing code (methods) to translate JTYPEs to ETYPEs and back.
  220.  
  221.   4. (Optional) Providing destructor code for ETYPEs (to release
  222.      embedded resources.)
  223.  
  224.   5. (Optional) Providing code to allocate and free ETYPEs.
  225.  
  226. In turn, the VM has the responsibility to call the appropriate hook
  227. methods at the right time whenever ETYPEs are passed to or from COM
  228. methods.  Once the hook class is written, the VM automatically supports
  229. passing ETYPEs (if ETYPE is fixed-size), ETYPE*, and ETYPE**, as well as
  230. returning ETYPE*.  The VM also implements the correct semantics for "[in]",
  231. "[out]", "[in,out]" and "[out,retval]."  These are typelib attributes
  232. which JACTIVEX passes along via the extra bits in the .class file.  An "[out]"
  233. or double indirection is mapped to a one-element array of JTYPE rather than
  234. to JTYPE itself. 
  235.  
  236.  
  237.  
  238. HOOK CLASS IMPLEMENTATION
  239. =========================
  240. Although hook classes are packaged as Java classes, they cannot be written
  241. in Java.  Hook classes must receive and pass machine addresses (pointers).
  242. Java methods can receive pointers by masquerading them as an int, but the
  243. Java language offers no way to dereference, allocate, destroy or otherwise
  244. manipulate pointers.  Thus, use of the Raw Native Interface (RNI) is
  245. virtually a necessity for authoring hook classes.  RNI allows Java methods
  246. to be implemented as C functions inside a DLL.  RNI also offers a small set
  247. of APIs that can be called from C to create, manipulate and destroy Java
  248. objects.
  249.  
  250. Why not use the Java/COM layer itself to author hook classes by
  251. implementing the methods as a COM inproc server and exposing them as Java?
  252. Actually, this approach is possible; however, the Java/COM layer was
  253. designed to insulate the called method from the fact that Java is involved.
  254. This makes it more difficult for the called method to create, manipulate and
  255. destroy Java objects, which is something that hook classes also need to do.
  256.  
  257. RNI, although more low-level, is usually the preferred approach for writing
  258. code that bridges Java and COM, which is what a hook class does.  Therefore, 
  259. this document will assume that RNI is being used to author hook classes.
  260.  
  261.  
  262.  
  263. RAW NATIVE INTERFACE BASICS
  264. ===========================
  265. This section covers the basics of RNI so you can get started; however,
  266. a full discussion of the RNI API set is well beyond scope of this document.
  267. RNI is fully documented in the Java SDK.  If you are familiar with RNI,
  268. you can skip this section.
  269.  
  270. Here is a simple Java class with two methods: a normal Java method (Add)
  271. and an RNI method (AddRNI):
  272.  
  273.   package MyPackage.MySubPackage;
  274.  
  275.   class SampleRNI
  276.   {
  277.     public static int Add(int x, int y)
  278.     {
  279.        return x + y;
  280.     }
  281.  
  282.     public static native int AddRNI(int x, int y);
  283.  
  284.     static
  285.     {
  286.       System.loadLibrary("SampleRNICode");
  287.     }
  288.   }  
  289.  
  290. The AddRNI method uses the modifier "native" to declare that the method
  291. is implemented in C (or C++).  The static initializer of the class loads
  292. the DLL SampleRNICode.dll.  This DLL exports a function named
  293. MyPackage_MySubPackage_SampleRNI_AddRNI which is implemented as follows:
  294.  
  295.  
  296.         #include "native.h";  // RNI declarations (part of Microsoft SDK for
  297.                               // Java)
  298.  
  299.         // All RNI DLLs must export this method - otherwise, an
  300.         // UnsatisfiedLinkError will be thrown when the method is called.
  301.         // This a new requirement for the Microsoft VM for Java.
  302.         // Previous releases did not require this export. 
  303.         __declspec(dllexport)
  304.         DWORD __cdecl RNIGetCompatibleVersion()
  305.         {
  306.             return RNIVER;
  307.         }
  308.  
  309.  
  310.         __declspec(dllexport)
  311.         int
  312.         __cdecl
  313.         MyPackage_MySubPackage_SampleRNI_AddRNI(OBJECT*nil, int x, int y)
  314.         {
  315.             return x + y;
  316.         }
  317.  
  318.  
  319. When the SampleRNI class is first loaded, the VM invokes the static
  320. initializer block, which uses the system.loadLibrary() API to locate
  321. and load SampleRNICode.dll into the internal RNI DLL list of the VM.
  322. It then verifies that SampleRNICode.dll exports the public function
  323. "RNIGetCompatibleVersion", and that this function returns a version
  324. number compatible with the VM.
  325.  
  326. When the AddRNI method is actually invoked, the VM searches all
  327. DLLs loaded via system.loadLibrary() for one that exports the name
  328. MyPackage_MySubPackage_SampleRNI_AddRNI (naturally, the result of this
  329. lookup is remembered for future calls.)  When this export is found, the VM
  330. invokes it, passing the Java parameters. The return value of this
  331. C function becomes the value returned by the AddRNI method.
  332.  
  333. The "nil" argument declared by MyPackage_MySubPackage_SampleRNI_AddRNI
  334. will always be NULL for a static method.  If this were a non-static method,
  335. the nil parameter would hold a pointer to the instance being called
  336. (the "this" pointer.)  Static methods do not have a "this" pointer, but
  337. RNI still requires you to declare a dummy parameter as a place-holder.
  338.  
  339. The C function for RNI methods must be declared __cdecl (not __stdcall).
  340. Declaring them as __stdcall will un-balance the stack.
  341.  
  342. For the exports in a DLL to be visible for RNI linkage, the DLL must
  343. have been loaded using System.loadLibrary(). There is no way to prevent
  344. RNI from searching for names in DLLs loaded by other unrelated classes.
  345. RNI linkage depends entirely on the uniqueness of the export name formed
  346. from the package hierachy, class name, and method name.
  347.  
  348. In general, the msjavah tool can be used to generate a C header
  349. file containing the appropriate function declarations corresponding to a
  350. class.  For writing hook classes, it is easier to use the provided
  351. template.c file for the methods.   
  352.  
  353.  
  354.  
  355. STARTING A HOOK CLASS
  356. =====================
  357. The easiest way to start is by copying the files under the template
  358. subdirectory to a new directory. The key files are:
  359.  
  360.      TemplateMarshaler.java - a skeleton hook class
  361.      TemplateMarshaler.c    - contains a blank C function for each method
  362.      TemplateMarshaler.def  - exports the C functions in template.c
  363.  
  364. To make the files compilable, you must follow the instructions in each
  365. of the comments marked "TODO" in TemplateMarshaler.java and
  366. TemplateMarshaler.c. This involves replacing ETYPE and JTYPE with your
  367. particular type, and uncommenting some optional methods.
  368.  
  369. The template contains C functions for eight hook methods. The comments
  370. inside each function describe what that hook function is supposed to do.
  371. We will go into more detail about this as we proceed through the examples.
  372. First, a few salient points about the hook functions as a whole:
  373.  
  374.    - Each C function starts with an unused parameter "OBJECT*x". This is
  375.      an artifact of RNI and will always be NULL for a static method. This
  376.      parameter can be ignored.
  377.  
  378.    - Several C functions have parameters "JTYPE javaval". "JTYPE" should
  379.      be replaced throughout by the C type that represents your Java type.
  380.      To find out what C type to use, consult the table in TemplateMarshaler.c
  381.      or use msjavah.
  382.  
  383.    - Several C functions have parameters "PPETYPE ppetype" (a pointer to
  384.      a pointer to an ETYPE).  These parameters are represented as "int"
  385.      in the Java method, but they are true pointers.  IMPORTANT: All
  386.      pointers passed to hook methods are double-indirect ETYPE (PPETYPE).
  387.      For many methods (such as releaseExternal), this will seem like an
  388.      unnecessary level of indirection.  The reason ETYPE is always
  389.      double-indirected has to do with an obscure feature of hook classes
  390.      that allows you to redefine the size and nature of A POINTER to
  391.      ETYPE.  This is a very arcane use of hook classes and will not be
  392.      mentioned again in this primer.  Just remember that you will, in
  393.      some cases, have to deal with an extra level of indirection because
  394.      of this.
  395.  
  396.    - Except for the functions that return Java types, hook methods return
  397.      VOID or return a native pointer through an output buffer.  To indicate
  398.      failures, hook methods should use SignalError() or SignalErrorPrintf()
  399.      which will cause RNI to throw a Java exception upon the exit of the 
  400.      C function.  Remember that SignalError() and SignalErrorPrintf()
  401.      return to their callers, so you must arrange to exit from your C
  402.      function immediately after calling these APIs.
  403.  
  404.  
  405.  
  406. EXAMPLE 1: FixedPtMarshaler (THE BASIC HOOK CLASS).
  407. ===================================================
  408. The FixedPtMarshaler is about the simplest useful hook class.  It
  409. exposes only two methods and one field. The JTYPE is "double", and
  410. the ETYPE is the Win32 FIXED structure for representing fixed point
  411. fractions:
  412.  
  413.         struct FIXED {
  414.             WORD        fract;
  415.             short       value;
  416.         } FIXED;
  417.  
  418.  
  419. The source code for FixedPtMarshaler can be found under the fixed
  420. directory. The executables (FixedPtMarshaler.class and FixedPtMarshaler.dll)
  421. are under the rundir directory.
  422.  
  423.     The members implemented by FixedPtMarshaler are:
  424.  
  425.       public FixedPtMarshaler {
  426.  
  427.                                         
  428.           public static int cbByValSize; // set of sizeof(FIXED) = 8
  429.  
  430.           public static double toJava(int ppFIXED, int flags)
  431.           {
  432.               convert **ppFIXED to a double
  433.               return the double
  434.           }
  435.  
  436.           public static void copyToExternal(double javaval,
  437.                                             int    ppFIXED,
  438.                                             int    Flags)
  439.           {
  440.               convert double to a FIXED
  441.               copy the FIXED to **ppFIXED
  442.           }
  443.           
  444.  
  445.       }
  446.  
  447.  
  448. Even with only these methods, this hook class can now be used in the
  449. following ways:
  450.  
  451.     COM                         becomes         Java
  452.     ---------------------------------------     ---------------------------
  453.     HRESULT func([in] FIXED)                    func(double)
  454.     HRESULT func([out,retval] FIXED*)           double func()
  455.     HRESULT func([in] FIXED*)                   func(double)
  456.     HRESULT func([out] FIXED*)                  func(double[])
  457.     HRESULT func([in,out] FIXED*)               func(double[])
  458.  
  459. Note that when using this hook class in a JNF file, you should preceed
  460. the Java type "double" with the "const" modifier: i.e.
  461.  
  462.     [Custom]
  463.     FIXED=const double, FixedPtMarshaler
  464.  
  465. If you do not include the "const" modifier, JACTIVEX will assume that you
  466. wish to implement "[out]" parameters by side-effecting a value passed
  467. in by the caller, rather than passing a new value out via a 1-element
  468. array.  That is, JACTIVEX would map
  469.  
  470.     HRESULT func([out] FIXED*)
  471.  
  472. to
  473.  
  474.     func(double)           // Not what you wanted!
  475.  
  476.     
  477. Because "double" cannot be side-effected (it is immutable), this
  478. method prototype cannot possibly return a value to the caller.
  479.  
  480.  
  481.  
  482. EXAMPLE 2: VarStrMarshaler (EMBEDDED RESOURCES).
  483. ================================================
  484. The basic hook class assumes that ETYPE contained no embedded pointers
  485. or handles to allocated resources that need to be freed when ETYPE is no
  486. longer needed. In C++ terms, if ETYPE was a class, it was assumed ETYPE
  487. did not have a destructor. Some structures, however, do need to clean up
  488. embedded resources. A well known example is the VARIANT structure
  489. used in Automation. A VARIANT is a fixed-size structure, yet it can have
  490. allocated objects such as BSTRs, SAFEARRAYSs and COM objects hanging
  491. off it. The "destructor" for a VARIANT is the VariantClear API, which
  492. checks the type of the variant and performs the appropriate cleanup
  493. (freeing the BSTR, freeing the SAFEARRAY, calling Release on the COM
  494. object, etc.)
  495.  
  496. Arranging for proper cleanup of embedded resources requires only
  497. one new method: releaseByValExternal.
  498.  
  499. The VarStr marshaler maps VARIANTs, confining itself to only one
  500. case: BSTR Variants, to Java String objects. That is, the JTYPE is
  501. String, and the ETYPE is:
  502.  
  503.         struct {
  504.             short vt;           // Always VT_BSTR for this example
  505.             short unused;
  506.             short unused1;
  507.             short unused2;
  508.             BSTR  bstrVal;      // Points to characters in BSTR
  509.             long  unused3;      // never used in this example.
  510.  
  511.         } VARIANT;
  512.  
  513.  
  514. The source code for VarStrMarshaler can be found under the varstr
  515. directory. The executables (VarStrMarshaler.class and VarStrMarshaler.dll)
  516. are under the rundir directory.
  517.  
  518.     The members implemented by VarStrMarshaler are:
  519.  
  520.       public VarStrMarshaler {
  521.  
  522.                                         
  523.           public static int cbByValSize; // set of sizeof(VARIANT) = 16
  524.  
  525.           public static String toJava(int ppVARIANT, int flags)
  526.           {
  527.               convert **ppVARIANT to a String
  528.               return the String
  529.           }
  530.  
  531.           public static void copyToExternal(String javaval,
  532.                                             int    ppVARIANT,
  533.                                             int    Flags)
  534.           {
  535.               convert String to a VARIANT
  536.               copy the VARIANT to **ppVARIANT
  537.           }
  538.  
  539.           /* NEW! */
  540.           public static void releaseByValExternal(int ppVARIANT, int Flags)
  541.           {
  542.               SysStringFree( (*ppVARIANT)->bstrVal );
  543.           }
  544.           
  545.  
  546.       }
  547.  
  548.     This hook class can be used in the following ways.
  549.  
  550.     COM                         becomes         Java
  551.     ---------------------------------------     ---------------------------
  552.     HRESULT func([in] VARIANT)                    func(String)
  553.     HRESULT func([out,retval] VARIANT*)           String func()
  554.     HRESULT func([in] VARIANT*)                   func(String)
  555.     HRESULT func([out] VARIANT*)                  func(String[])
  556.     HRESULT func([in,out] VARIANT*)               func(String[])
  557.  
  558. As before, String is immutable so the "const" modifier should be used
  559. for it in the JNF file.
  560.  
  561.  
  562.  
  563. EXAMPLE 3: PointMarshaler (NON-"CONST" JAVA OBJECTS).
  564. ====================================================
  565. Up till now, all JTYPEs were immutable. This is why "[out]" parameters
  566. could only be handled by one element arrays: that is, a caller had to write:
  567.  
  568.         {
  569.            double ad1[] = {0};
  570.            double ad2[] = {0};
  571.  
  572.            func(ad1, ad2);
  573.  
  574.            System.out.println("func gave back: " + ad1[0] + ", " + ad1[1]);
  575.  
  576.         }
  577.  
  578.     It should be clear that the function:
  579.  
  580.         HRESULT func([out] FIXED *, [out] FIXED *);
  581.  
  582.     could not be usefully mapped to:
  583.  
  584.         func(double d1, double d2);
  585.  
  586.     because "func" would have no way of returning information to the caller.
  587.  
  588. However, consider the java class java.awt.Point (which represents a
  589. point in 2-dimensional space.) The Point class is mutable as its "x" and "y"
  590. fields are public and can set by anyone. Therefore, it is advantageous
  591. to use the non-array form for passing [out] parameters. To do this only
  592. requires wo new methods "copyToJava" and "toUninitJava".
  593.  
  594. The PointMarshaler class maps java.awt.Point to the Win32 POINT
  595. structure. The source code for PointMarshaler can be found under the point
  596. directory. The executables (PointMarshaler.class and PointMarshaler.dll)
  597. are under the rundir directory.
  598.  
  599.     The members implemented by PointMarshaler are:
  600.  
  601.       public PointMarshaler {
  602.  
  603.                                         
  604.           public static int cbByValSize; // set of sizeof(POINT) = 8
  605.  
  606.           public static Point toJava(int ppPOINT, int flags)
  607.           {
  608.               convert **ppPOINT to a Point
  609.               return the Point
  610.           }
  611.  
  612.           public static void copyToExternal(Point javaval,
  613.                                             int    ppPOINT,
  614.                                             int    Flags)
  615.           {
  616.               convert Point to a POINT
  617.               copy the POINT to **ppPOINT
  618.           }
  619.  
  620.           /* NEW! */
  621.           public static void copyToJava(Point javaval, int ppPOINT, int Flags)
  622.           {
  623.               modify "javaval" in place so it is "equivalent" to **ppPOINT;
  624.           }
  625.  
  626.           /* NEW! */
  627.           public static Point toUninitJava(int ppPOINT, int flags)
  628.           {
  629.               create a new Point with arbitrary x and y values.
  630.               the contents of **ppPOINT are completely undefined and should
  631.               be ignored for fixed-size hook classes.
  632.           }
  633.       }
  634.  
  635.     This hook class can be used in the following ways.
  636.  
  637.     COM                         becomes         Java
  638.     ---------------------------------------     ---------------------------
  639.     HRESULT func([in] POINT)                    func(Point)
  640.     HRESULT func([out,retval] POINT*)           Point func()
  641.     HRESULT func([in] POINT*)                   func(Point)
  642.     HRESULT func([out] POINT*)                  func(Point)
  643.     HRESULT func([in,out] POINT*)               func(Point)
  644.  
  645. This differs from the basic hook class in that "[out] POINT*" becomes
  646. Point rather than Point[]. To force JACTIVEX to generate the non-array
  647. mapping, you must *omit* the const modifier from "Point" in the JNF file.
  648.  
  649.  
  650.  
  651. EXAMPLE 4: RectMarshaler (SUPPORT FOR CUSTOM ALLOCATION).
  652. =========================================================
  653. The hook classes up to this point have provided no method for allocating
  654. or freeing the memory for ETYPE itself. They have relied entirely on the
  655. VM to allocate the actual memory for ETYPE. As a result, the use of
  656. custom datatypes up till now has been restricted to cases where ETYPE can
  657. be allocated on the stack and its lifetime is bounded by the lifetime of the
  658. call. While this is sufficient for some types, there are cases where stack
  659. allocation is insufficient. The first case is when there is need to marshal
  660. methods where an ETYPE is allocated by the callee but freed by the caller
  661. (or vice-versa, as can happen with [in,out] paramaters.) The second case is
  662. if the datatype is variable-sized (such as a string), in which case, the
  663. VM cannot do the stack-allocation because it does not know the size.
  664. We will consider the first case here, and the second case later.
  665.  
  666. It is the responsibility of the hook class to specify which API is used to
  667. allocate and release the memory for ETYPE.  The new hook methods required
  668. to support this are "toExternal" and "releaseExternal".
  669.  
  670. For this example, we will marshal the Win32 RECT structure into
  671. java.awt.Rect objects. The source code for RectMarshaler can be found
  672. under the fixed directory. The executables (RectMarshaler.class and
  673. RectMarshaler.dll) are under the rundir directory.
  674.  
  675.     The members implemented by RectMarshaler are:
  676.  
  677.       public RectMarshaler {
  678.  
  679.                                         
  680.           public static int cbByValSize; // set of sizeof(RECT) = 8
  681.  
  682.           public static Rect toJava(int ppRECT, int flags)
  683.           {
  684.               convert **ppRECT to a Rect
  685.               return the Rect
  686.           }
  687.  
  688.           public static void copyToExternal(Rect javaval,
  689.                                             int    ppRECT,
  690.                                             int    Flags)
  691.           {
  692.               convert Rect to a RECT
  693.               copy the RECT to **ppRECT
  694.           }
  695.  
  696.  
  697.           /*NEW*/
  698.           public static void toExternal(Rect javaval, int ppRECT, int Flags)
  699.           {
  700.               allocate a new RECT, initialize it using javaval,
  701.               store pointer in *ppRECT
  702.           }
  703.  
  704.  
  705.           /*NEW*/
  706.           public static void releaseExternal(int ppRECT, int Flags)
  707.           {
  708.               release *ppRECT. If RECT required a releaseByValExternal
  709.               (which it doesn't), this routine must do that work as well.
  710.           }
  711.           
  712.  
  713.       }
  714.  
  715. Note we could also have implemented copyToJava and toUninitJava here since
  716. Rect is mutable (like Point). However, that would just cloud the issue
  717. here.
  718.  
  719. IMPORTANT NOTE: even in the presence of the new methods, the VM will
  720. optimize away certain allocations by allocating the data structure on
  721. the stack (for example, for "[in] ETYPE*" calls, the COM method will receive
  722. a pointer to a VM-allocated ETYPE on the stack rather than one allocated
  723. by toExternal()). If you want ALL ETYPEs to be allocated using toExternal(),
  724. you must *omit* the cbByValSize field. This will prevent the VM from
  725. optimizing to stack allocations as it cannot know how many bytes to allocate.
  726.  
  727.  
  728.    This hook class can be used in the following ways:
  729.  
  730.     COM                         becomes         Java
  731.     ---------------------------------------     ---------------------------
  732.     HRESULT func([in] RECT)                     func(Rect)
  733.     HRESULT func([out,retval] RECT*)            Rect func()
  734.     HRESULT func([in] RECT*)                    func(Rect)
  735.     HRESULT func([out] RECT*)                   func(Rect[])
  736.     HRESULT func([in,out] RECT*)                func(Rect[])
  737.     HRESULT func([out,retval] RECT**)           Rect func()
  738.     HRESULT func([in] RECT**)                   func(Rect[])
  739.     HRESULT func([out] RECT**)                  func(Rect[])
  740.     HRESULT func([in,out] RECT**)               func(Rect[])
  741.  
  742.  
  743.  
  744. EXAMPLE 5: AnsiMarshaler (Variable-sized data types).
  745. =========================================================
  746. So far, all of the ETYPE examples have been fixed-size structures. For
  747. some data structures, this is a prohibitive limitation. The classic example
  748. is a simple null-terminated string.
  749.  
  750. To define a variable-sized structure, the hook class must omit the
  751. cbByValSize field. The absence of this field marks the hook class as
  752. variable-size. Unlike a fixed-sized hook class, a variable-size hook class
  753. must support toExternal and releaseExternal in order to be useful (the
  754. VM cannot stack-allocate a variable-sized structure). In addition
  755. certain mappings available to fixed-size hooks (in particular, those
  756. that pass the datatype by value) are not available to variable-size hooks. 
  757.  
  758. More specifically, let us consider the simple case where ETYPE is char
  759. (not LPSTR!), and JTYPE is java.lang.String. We will consider both
  760. types immutable. This structure can be defined by a hook class with only
  761. three methods: toJava, toExternal and releaseExternal; the minimum useful
  762. variable-size hook class.
  763.  
  764. The AnsiMarshaler class maps Strings to the LPSTR (ANSI null-terminated
  765. strings.). The source code for AnsiMarshaler can be found under the ansistr
  766. directory. The executables (AnsiMarshaler.class and AnsiMarshaler.dll)
  767. are under the rundir directory.
  768.  
  769.     The members implemented by AnsiMarshaler are:
  770.  
  771.       public AnsiMarshaler {
  772.  
  773.                                         
  774.           public static String toJava(int ppCHAR, int flags)
  775.           {
  776.               convert **ppCHAR to a String
  777.               return the String
  778.           }
  779.  
  780.           public static void toExternal(String javaval, int ppCHAR, int Flags)
  781.           {
  782.               allocate a new LPSTR, initialize it using javaval,
  783.               store pointer in *ppCHAR
  784.           }
  785.  
  786.  
  787.           public static void releaseExternal(int ppCHAR, int Flags)
  788.           {
  789.               release *ppCHAR. If LPSTR required a releaseByValExternal
  790.               (which it doesn't), this routine must do that work as well.
  791.           }
  792.           
  793.  
  794.       }
  795.  
  796.     The allowed usages are:
  797.  
  798.  
  799.     COM                         becomes         Java
  800.     ---------------------------------------     ---------------------------
  801.     HRESULT func([in] CHAR*)                    func(String)
  802.     HRESULT func([out] CHAR*)                   func(String[])
  803.     HRESULT func([in,out] CHAR*)                func(String[])
  804.     HRESULT func([out,retval] CHAR**)           String func()
  805.     HRESULT func([in] CHAR**)                   func(String[])
  806.     HRESULT func([out] CHAR**)                  func(String[])
  807.     HRESULT func([in,out] CHAR**)               func(String[])
  808.  
  809.  
  810.  
  811. RUNNING AND BUILDING THE SAMPLE CLIENT
  812. =========================================================
  813. The "client" subdirectory contains the sources for a sample Java client
  814. and a C++ inproc COM server which exchange all the various types we have
  815. implemented.
  816.  
  817. To run the example, simply change the current directory to "rundir",
  818. and type "GO".
  819.  
  820. To build the sample, you will need to provide a makefile compatible with
  821. your build environment. Here are the steps:
  822.  
  823.  
  824.    1. Compile sysdata.odl to produce sysdata.tlb:
  825.  
  826.         midl /mktyplib203 sysdata.odl.
  827.  
  828.  
  829.  
  830.    2. Compile CustSample.odl to produce CustSample.tlb and CustSample.h:
  831.  
  832.         mkdir debug
  833.         midl /mktyplib203 /h CustSample.h /out debug CustSample.odl
  834.  
  835.  
  836.  
  837.    3. Compile CustSample.tlb to produce CCustSample.java and ICustSample.java:
  838.  
  839.         JACTIVEX debug\CustSample.tlb -javatlb -nCustSample.jnf -d . /p:b-
  840.  
  841.  
  842.  
  843.    4. Compile CustSample.java to produce CustSample.class and callbacks.class:
  844.  
  845.         JVC CustSample  (You must use JVC version 1.02.3920 or later)
  846.  
  847.  
  848.  
  849.    5. Compile and link CustSample.cpp & CustSample.def to produce CustSample.dll:
  850.         
  851.         cl /LD CustSample.cpp oleaut32.lib uuid.lib advapi32.lib user32.lib ole32.lib CustSample.def
  852.  
  853.  
  854.  
  855.    6. Copy the server and class files to the run location:
  856.  
  857.         copy *.dll ..\rundir
  858.         copy *.class ..\rundir
  859.   
  860.  
  861.  
  862.    7. Follow the build steps in the README.TXT files in each of the following directories:
  863.  
  864.         ansistr
  865.         fixedpt
  866.         point
  867.         rect
  868.         varstr
  869.  
  870.  
  871.  
  872.    8. Run the sample:
  873.  
  874.         cd ..\rundir
  875.         RegSvr32 CustSample.dll
  876.         jview -cp:p . CustSample
  877.         RegSvr32 -u CustSample.dll
  878.