将静态Windows库转换为DLL

12

我有一个库,其中包含一堆静态的*lib文件,我希望能够从JNA(一个允许在Java代码中动态调用dll的Java库)中访问它们,那么是否有一种神奇的方法可以将静态库变成dll?

代码是使用Visual Studio编译的(希望这是相关的),我还有适当的头文件。

我没有访问源代码的权限,而且我希望只使用免费(指价格)的工具来完成它。


你至少有库头文件吗? - anon
是的,我有头文件。 - jb.
只有静态库文件有点不寻常。你确定没有可用的 DLL 吗?这些库来自哪里? - jdigital
我没有找到任何东西。这是针对一些光谱设备的,该库非常古老(来自2000年左右)。 - jb.
3个回答

13

我不知道是否有任何工具可以自动完成这个过程,但是步骤是创建一个 DLL 项目并将您的库添加到该项目中。对于头文件中的每个函数:

int SomeLibFunc( int x, int y );

你需要在 DLL 中创建并导出自己的函数;

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

这个过程非常机械化,你可以使用像Perl这样的工具编写脚本来创建DLL源文件。


我已经写了一个类来实现这个功能。我会对它进行优化并将其发布在这个问题的某个地方。 - jb.
3
不需要使用MyFunc函数,你可以简单地向项目添加一个.def文件来显式地导出符号。但要注意名称混淆问题。参见http://msdn.microsoft.com/en-us/library/d91k01sh(VS.80).aspx。 - iain

4

假设您无法访问源代码,您可以简单地创建一个包装DLL,该DLL导出您需要的函数并委派给静态库。


有没有自动完成的方法(这个库非常大),还是我必须手动完成? - jb.
我不知道这样的方法,但是通过一点脚本编写,你可能可以轻松地生成所有内容。 - On Freund
2
如果您正在使用.lib文件,则可能已经有一个头文件。进行一些正则表达式处理,您应该能够提取函数定义,然后使用这些定义来构建包装函数。听起来像是一个不错的下午小项目。同时最好自动生成.def文件... - Kevin Day

1

我按照匿名建议的做法,编写了一个自动转换器(有人建议在声明之前加上_ddlspec(export)并使用此头文件编译dll会起作用--但实际上并没有--也许是我做错了什么--我只是一个普通的Java程序员 ;)):

它基本上解析头文件并将其转换为:

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

to:

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

这里是代码(很可能是正则表达式滥用,但它能工作),GUI部分取决于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;
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接