views:

2050

answers:

2

Question:

I have a library contains a bunch of static *lib files, I wish to access them from JNA (a Java library that allows one to dynamically call `dll's from JAVA Code), so is there a way to magically change static lib to dll?

Code was compiled using Visual studio (hope that is relevant), and I also have appropriate header files.

I do not have access to source code, also I would like to do it using only free (as in beer) tools.

Solution:

I did as Neil Butterworth suggested, i did an automatic converter (someone suggested just putting _ddlspec(export) before declaration and compiling dll with this header would work -- well it didn't -- maybe i did something wrong -- I'm Plain Old Java Programmer ;)):

it basically parses header files and turns:

  SADENTRY SadLoadedMidFiles( HMEM, USHORT usMaxMidFiles, VOID * );

to:

 __declspec(dllexport) SADENTRY DLL_WRAPPER_SadLoadedMidFiles(HMEM param0, 
            USHORT usMaxMidFiles, VOID* param2){
      return SadLoadedMidFiles(param0, usMaxMidFiles, param2);
  }

Here is the code (most probably its Regexp abuse, but it works), gui part depends on MigLayout:

    package cx.ath.jbzdak.diesIrae.util.wrappergen;

    import net.miginfocom.swing.MigLayout;

    import javax.swing.*;
    import static java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment;
    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.*;
    import java.nio.charset.Charset;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;

    /**
     * Displays a window. In this window you have to specify two things:
     * <p/>
     * 1. Name of header file that you want to process.
     * <p/>
     * 2. Name of output files extension will be added automatically. We will override any existing files.
     *
     * <p/>
     * Dependencies: MigLayout
     * <p/>
     * Actual wrapper generation is done inside WrapperGen class.
     * <p/>
     * KNOWN ISSUES:
     * <p/>
     * 1. Ignores preprocessor so may extract finction names that are inside <code>#if false</code>.
     * <p/>
     * 2. Ignores comments
     * <p/>
     * 3. May fail to parse werid parameter syntax. . .
     *
     * Created by IntelliJ IDEA.
     * User: Jacek Bzdak 
     */
    public class WrapperGenerator {

        public static final Charset charset = Charset.forName("UTF-8");


        WrapperGen generator = new WrapperGen();

        // GUI CODE:

        File origHeader, targetHeader, targetCpp;

        JTextField newHeaderFileName;

        JFrame wrapperGeneratorFrame;
        {
            wrapperGeneratorFrame = new JFrame();
            wrapperGeneratorFrame.setTitle("Zamknij mnie!"); //Wrapper generator
            wrapperGeneratorFrame.setLayout( new MigLayout("wrap 2, fillx", "[fill,min!]"));
            wrapperGeneratorFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            ActionListener buttonListener = new ActionListener() {
                JFileChooser fileChooser = new JFileChooser();
                {
                    fileChooser.setFileFilter(new javax.swing.filechooser.FileFilter() {
                        @Override
                        public boolean accept(File f) {
                            return f.isDirectory() || f.getName().matches(".*\\.h(?:pp)?");
                        }
                        @Override
                        public String getDescription() {
                            return "Header files";
                        }
                    });
                    fileChooser.setCurrentDirectory(new File("C:\\Documents and Settings\\jb\\My Documents\\Visual Studio 2008\\Projects\\dll\\dll"));
                }
                public void actionPerformed(ActionEvent e) {
                    if(JFileChooser.APPROVE_OPTION == fileChooser.showOpenDialog(wrapperGeneratorFrame)){
                        origHeader = fileChooser.getSelectedFile();
                    }
                }
            };
            wrapperGeneratorFrame.add(new JLabel("Original header file"));
            JButton jButton = new JButton("Select header file");
            jButton.addActionListener(buttonListener);
            wrapperGeneratorFrame.add(jButton);
            wrapperGeneratorFrame.add(new JLabel("Result files prefix"));
            newHeaderFileName = new JTextField("dll_wrapper");
            wrapperGeneratorFrame.add(newHeaderFileName);
            ActionListener doListener = new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    targetHeader = new File(origHeader.getParentFile(), newHeaderFileName.getText() + ".h");
                    targetCpp = new File(origHeader.getParentFile(), newHeaderFileName.getText() + ".cpp");
                    try {
                        targetHeader.createNewFile();
                        targetCpp.createNewFile();
                        generator.reader = new InputStreamReader(new FileInputStream(origHeader),charset);
                        generator.cppWriter = new OutputStreamWriter(new FileOutputStream(targetCpp), charset);
                        generator.heaerWriter = new OutputStreamWriter(new FileOutputStream(targetHeader), charset);
                        generator.parseReader();
                    } catch (IOException e1) {
                        e1.printStackTrace();
                        JOptionPane.showMessageDialog(wrapperGeneratorFrame, "ERROR:" + e1.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
                        return;
                    }
                }
            };
            JButton go = new JButton("go");
            go.addActionListener(doListener);
            wrapperGeneratorFrame.add(go, "skip 1");
        }

        public static void main(String []args){
           SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                   WrapperGenerator wgen = new WrapperGenerator();
                   JFrame f = wgen.wrapperGeneratorFrame;
                   wgen.wrapperGeneratorFrame.pack();
                   Point p = getLocalGraphicsEnvironment().getCenterPoint();
                   wgen.wrapperGeneratorFrame.setLocation(p.x-f.getWidth()/2, p.y-f.getHeight()/2);
                   wgen.wrapperGeneratorFrame.setVisible(true);
               }
           });
        }
    }

    /**
     * Does the code parsing and generation
     */
    class WrapperGen{

        /**
         * Method is basically syntax like this: <code>(anything apart from some special chars like #;) functionName(anything)</code>;
         * Method declarations may span many lines.
         */
        private static final Pattern METHOD_PATTERN =
                                 //1          //2              //params
                Pattern.compile("([^#;{}]*\\s+\\w[\\w0-9_]+)\\(([^\\)]*)\\);", Pattern.MULTILINE);
        //1 - specifiers - including stuff like __dllspec(export)...
        //2 - function name
        //3 param list

        /**
         * Generated functions will have name prefixet with #RESULT_PREFIX
         */
        private static final String RESULT_PREFIX = "DLL_WRAPPER_";

        /**
         * Specifiers of result will be prefixed with #RESULT_SPECIFIER
         */
        private static final String RESULT_SPECIFIER =  "__declspec(dllexport) ";

        Reader reader;

        Writer heaerWriter;

        Writer cppWriter;

        public void parseReader() throws IOException {
            StringWriter writer = new StringWriter();
            int read;
            while((read = reader.read())!=-1){
                writer.write(read);
            }
            reader.close();
            heaerWriter.append("#pragma once\n\n\n");
            heaerWriter.append("#include \"stdafx.h\"\n\n\n"); //Standard Visual C++ import file.
            cppWriter.append("#include \"stdafx.h\"\n\n\n");
            Matcher m = METHOD_PATTERN.matcher(writer.getBuffer());
            while(m.find()){
                System.out.println(m.group());
                handleMatch(m);
            }
            cppWriter.close();
            heaerWriter.close();
        }

        public void handleMatch(Matcher m) throws IOException {
            Method meth = new Method(m);
            outputHeader(meth);
            outputCPP(meth);
        }

        private void outputDeclaration(Method m, Writer writer) throws IOException {
            //writer.append(RESULT_SPECIFIER);
            writer.append(m.specifiers);
            writer.append(" ");
            writer.append(RESULT_PREFIX);
            writer.append(m.name);
            writer.append("(");
            for (int ii = 0; ii < m.params.size(); ii++) {
                Parameter p =  m.params.get(ii);
                writer.append(p.specifiers);
                writer.append(" ");
                writer.append(p.name);
                if(ii!=m.params.size()-1){
                    writer.append(", ");
                }
            }
            writer.append(")");
        }

        public void outputHeader(Method m) throws IOException {

            outputDeclaration(m, heaerWriter);
            heaerWriter.append(";\n\n");
        }

        public void outputCPP(Method m) throws IOException {
            cppWriter.append(RESULT_SPECIFIER);
            outputDeclaration(m, cppWriter);
            cppWriter.append("{\n\t");
            if (!m.specifiers.contains("void") || m.specifiers.matches(".*void\\s*\\*.*")) {
                cppWriter.append("return ");
            }
            cppWriter.append(m.name);
            cppWriter.append("(");
            for (int ii = 0; ii < m.params.size(); ii++) {
                Parameter p =  m.params.get(ii);
                cppWriter.append(p.name);
                if(ii!=m.params.size()-1){
                    cppWriter.append(", ");
                }
            }
            cppWriter.append(");\n");
            cppWriter.append("}\n\n");
        }

    }

    class Method{
        private static final Pattern NAME_REGEXP =
                                     //1      //2
                Pattern.compile("\\s*(.*)\\s+(\\w[\\w0-9]+)\\s*", Pattern.MULTILINE);
        //1 - all specifiers - including __declspec(dllexport) and such ;)
        //2 - function name

        public final List<Parameter> params;

        public final String name;

        public final String specifiers;

        public Method(Matcher m) {
            params = Collections.unmodifiableList(Parameter.parseParamList(m.group(2)));
            Matcher nameMather = NAME_REGEXP.matcher(m.group(1));
            System.out.println("ALL: " + m.group()); 
            System.out.println("G1: " + m.group(1));
            if(!nameMather.matches()){
                throw new IllegalArgumentException("for string "  + m.group(1));
            }
    //        nameMather.find();
            specifiers = nameMather.group(1);
            name = nameMather.group(2);
        }
    }

    class Parameter{

        static final Pattern PARAMETER_PATTERN =
                                      //1           //2
                Pattern.compile("\\s*(?:(.*)\\s+)?([\\w\\*&]+[\\w0-9]*[\\*&]?)\\s*");
        //1 - Most probably parameter type and specifiers, but may also be empty - in which case name is empty, and specifiers are in 2
        //2 - Most probably parameter type, sometimes prefixed with ** or &* ;), also
        // 'char *' will be parsed as grup(1) == char, group(2) = *.

        /**
         * Used to check if group that represenrs parameter name is in fact param specifier like '*'.
          */
        static final Pattern STAR_PATTERN =
                Pattern.compile("\\s*([\\*&]?)+\\s*");

        /**
         * If
         */
        static final Pattern NAME_PATTERN =
                Pattern.compile("\\s*([\\*&]+)?(\\w[\\w0-9]*)\\s*");

        public final String name;
        public final String specifiers;

         public Parameter(String param, int idx) {
            System.out.println(param);
            Matcher m = PARAMETER_PATTERN.matcher(param);
            String name = null;
            String specifiers = null;
            if(!m.matches()){
                throw new IllegalStateException(param);
            }
            name = m.group(2);
            specifiers = m.group(1);
            if(specifiers==null || specifiers.isEmpty()){ //Case that parameter has no name like 'int', or 'int**'
                specifiers = name;
                name = null;
            }else if(STAR_PATTERN.matcher(name).matches()){ //Case that parameter has no name like 'int *'
                specifiers += name;
                name = null;
            }else if(NAME_PATTERN.matcher(name).matches()){ //Checks if name contains part of type like '**ptrData', and extracts '**'
                Matcher m2 = NAME_PATTERN.matcher(name);
                m2.matches();
                if(m2.group(1)!=null){
                    specifiers += m2.group(1);
                    name = m2.group(2);
                }

            }
            if(name==null){
                name = "param" + idx;
            }
            this.specifiers = specifiers;
            this.name = name;
        }

        public static List<Parameter> parseParamList(String paramList){
            List<Parameter> result = new ArrayList<Parameter>();
            String[] params = paramList.split(",");
            int idx = 0;
            for(String param : params){
                Parameter p = new Parameter(param, idx++);
                result.add(p);
            }
            if(result.size()==1){
                Parameter p = result.get(0);
                if(p.specifiers.matches("\\s*void\\s*")){
                    return Collections.emptyList();
                }
            }
            return result;
        }
    }
+3  A: 

Assuming you don't have access to the source, you can simply create a wrapper DLL that exports the functions you need and delegates to the static library.

On Freund
Is there any way to do it automatically (this library is quite big) or I have to do it by hand?
jb
I'm not aware of such a way, but with a little scripting you can probably generate everything easily.
On Freund
If you are using the .lib, then you probably have a header file for it. A little bit of regex processing, and you should be able to pull out the function definitions, then use those to construct the wrapper functions. Sounds like a good little project for an afternoon. Might as well auto-generate the .def file at the same time...
Kevin Day
+7  A: 

I'm not aware of anyb tools that will do this automatically, but the process is to create a DLL project and add your libraries to the project. For each function in the header file:

int SomeLibFunc( int x, int y );

you would need to create and export your own function in the DLL;

int MyFunc( int x, int y ) {
   return SomLibFunc( x, y );
}

The process is quite mechanical, and you may be able to knock up a script using something like perl to create the DLL source files.

anon
Well i did a class that does it. I'll pettify it and post somewhere here int this question.
jb
Instead of using the function with MyFunc you could simply add a .def file to the project. This will explicitly export the symbols for you. Watch out for name mangling though. See http://msdn.microsoft.com/en-us/library/d91k01sh(VS.80).aspx
iain