C to Java Parser Sample
This sample is located in \Samples\JDirect\CToJParser.
Description
Using the Sample
Key Project Files
Technologies Demonstrated
Description
This parsing tool converts C function prototypes into Java wrappers for those functions and converts structures and trivial C++ classes into Java classes. It is designed to expose functions from dynamic-link libraries (DLLs) and provide access to them in Java using Microsoft® J/Direct. It handles all the J/Direct rules for fixed arrays, alignment issues, and string conversion for you.
The parsing tool performs the following translations or conversions from C or C++ to Java equivalents:
- Embedded fixed-length arrays in structures.
- Alignment.
- ASCII versus Unicode function and structure merging.
- Simple initialization of structure members.
- Keywords for modifying structure members.
- Expansion of nested structures to preserve the memory layout of structures.
This tool will not translate or convert the following:
- Macros. For #define mySample(x) x, mySample will not be recognized as a function.
- Nontrivial C++ classes (any that use inheritance or member functions)
- Unions.
- Structures with bitfields.
- 16-bit code.
- Passing structures by value instead of passing a pointer to the structure.
- COM objects.
The parser parses C code, builds up an accurate representation of the C code, and then applies some Java conversion heuristics. While this usually works for simple types, there may be special cases you want to handle. Therefore, you can edit Parser.java and add conversion rules. For example, you could increase Java performance by declaring some classes as final. However, since this was designed as a general tool, allowing customizability through subclassing was emphasized more than slight performance gains.
Inheritance is unsupported: J/Direct cannot currently correctly marshal the virtual function pointer.
Using the Sample
To use the C to Java parser, you need the following:
- A C preprocessor, such as cl, included with Microsoft® Visual C++®.
- Dumpbin, included with Microsoft® Developer Studio® (optional if you're only parsing one DLL).
- Your DLL.
- Header files for your DLL.
- A Java compiler and virtual machine that supports J/Direct.
- Grep (optional but recommended).
To run the parser
- Make sure your code is in an acceptable format. COM objects can't be parsed. Non-trivial classes can't be parsed, or are at least problematic. All exported functions should have WINAPI, APIENTRY, or CALLBACK (or another #define token that expands to __stdcall or __cdecl) between a function's return type and the function name, shown as follows:
BOOL WINAPI SwitchDesktop(HDESK hDesktop);
- Create symbol files for your .dll. (If you want to export only one DLL, you can ignore this step, but it is still recommended because skipping it adds one more step and substantial filtering.) Run this command on each DLL:
dumpbin /exports your_lib.dll > your_lib.sym
This list of symbols tells the parser in which classes your functions belong. All functions that aren't listed in a symbol file are put in Misc.java and need special attention (which is covered in this topic).
- Create Symbolfiles.txt in the same directory that the parser will be run in, listing all the symbol files you generated. Format of the file is one file per line, comment character is the number sign (#).
- Create a header file that includes all necessary headers.
For example, make a file, Gen.h, that includes everything you need. This is optional if you have only one header file to parse, you have one header file that includes all the other headers needed, or both. Also, ensure that your headers are included only once each, or if they are included multiple times that you wrap the headers with #ifdef statements. Your header should look like the following:
#ifdef __YOUR_HEADER_H__
#define __YOUR_HEADER_H__
<all of your header file code>
#endif
This is required to run the parser. It may also accelerate code compilation. The only time you don't want this is if you have a circular dependency in your header files (not recommended).
- Set up all the #define tokens you need for your code to compile in the current environment, and then run the C preprocessor over your header files. For example:
cl /Iinclude-path [/Dyour_symbol] /E gen.h > gen.prep
This generates a C preprocessed version of your headers with all the macros expanded, which is easier to parse.
- Edit the parser source for your files. Change the following to set up output files and packages:
- Change Parser.PackageName to whatever you want.
- Set Parser.ReadSymbols, Parser.Suppress_UnknownLib_Functions, and Parser.Suppress_Unused_Structures to reasonable values.
- In Parser.SetupOutputClasses(), add a PrintWriter for your class, output the name of the class to that file, and add the PrintWriter to the hash table, associating it with the name of your symbol file. Do this by cutting and pasting the code already there, changing the names around. Note that this is useless without a symbol file—all your output will go into Misc.java.
- Edit Parser.SetupFileFilters(), adding in any header files you parse to either the ExcludeHeaders or IncludeHeaders vectors as appropriate. By default, any unknown header file should be parsed, but this gives you a reasonable amount of control.
- Run the parser over your .prep file. Check the output for errors. Any error message starting with "Ack!" is a serious problem that will probably leave at least one function or structure out of the output. For example:
jview Parser gen.prep
- Check through Punt.txt, which should have been generated by the parser. It should contain a slightly convoluted list of all functions or structures the parser wouldn't translate or read in properly.
- If you didn't generate a symbol file, go back through Misc.java, pulling out all of your functions. Change "<unknown_library>" in every @dll.import line to your .dll file's name. There may be other functions in this class that might not be in your .dll file and if you erroneously say they are, you'll get linking errors.
- Test with jvc.
You can also use these tests:
- Get the size of the Java structure and compare it with the size of the C structure.
- Get the size of all the parameters passed to functions in Java and C.
Although these are reasonable first-pass tests, they don't guarantee correctness.
Using Define Scanner (defscan.java)
Defscan is a separate tool for scanning header files for #define constants.
To use defscan
- Use grep or a similar program to generate files containing only #define lines that span only one line with no other text in between. An example of using grep to do this is as follows:
grep #define your_header.h > your_header.def
- Scan for C-style comments that span multiple lines. If you don't do this, you may inadvertently comment out most of your constants. For any lines that pop up from the following command, fix them by adding a comment delimiter (*/) where it is needed.
grep \/\* your_header.def | grep -v \*\/
Defscan cannot parse macros, although a few simple ones have been provided for in the code. If you only have a handful of macros, you can add them to the code. Limited casting is supported. Strings can be output to the file or suppressed, depending on the value of defscan.SuppressStrings.
- Run defscan with the following command if you have a small number of files:
jview defscan your_file.def
- If there are many files, list them all in files and run defscan with no arguments.
The output is a set of interface files containing all the constants. Because this was written to handle the Microsoft® Win32® constants, the output files all start with win. They are listed in alphabetic order and by multiple interfaces (win[a-z].java), plus one master interface file that contains them all (win.java).
Key Project Files
Parser.java
Generates many of the Win32 API classes.
defscan.java
A simple scanner that searches header files for #define values and converts them to Java. Although it handles most of them, it can't process macros. Its output is an alphabetic list of Java interfaces.
Technologies Demonstrated
Microsoft J/Direct
- This sample exposes functions from DLLs and provides access to them in Java using J/Direct.
© 1999 Microsoft Corporation. All rights reserved. Terms of use.