将静态 windows 库转换为 dll

Posted

技术标签:

【中文标题】将静态 windows 库转换为 dll【英文标题】:Convert static windows library to dll 【发布时间】:2009-05-10 11:50:23 【问题描述】:

我有一个包含一堆静态*lib 文件的库,我希望从JNA(一个允许从JAVA 代码动态调用`dll's 的Java 库)访问它们,所以有没有办法神奇地将静态库更改为dll?

代码是使用 Visual Studio 编译的(希望是相关的),我也有相应的头文件。

我无权访问源代码,我也想只使用免费(如啤酒)工具来完成它。

【问题讨论】:

你至少有库头文件吗? 是的,我确实有头文件。 只有静态库文件有点不寻常。您确定没有可用的 DLL 吗?图书馆是从哪里来的? 嗯,我没有找到。这是一些光谱设备,图书馆很旧(从2000年开始)。 【参考方案1】:

我不知道有任何工具会自动执行此操作,但该过程是创建一个 DLL 项目并将您的库添加到项目中。对于头文件中的每个函数:

int SomeLibFunc( int x, int y );

您需要在 DLL 中创建和导出您自己的函数;

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

这个过程非常机械化,您可以使用 perl 之类的东西敲出一个脚本来创建 DLL 源文件。

【讨论】:

好吧,我做了一堂课。我将把它小化并在这个问题的某个地方发布。 您可以简单地将 .def 文件添加到项目中,而不是将函数与 MyFunc 一起使用。这将为您显式导出符号。不过要注意名称篡改。见msdn.microsoft.com/en-us/library/d91k01sh(VS.80).aspx【参考方案2】:

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

【讨论】:

有什么办法可以自动完成(这个库很大)还是我必须手动完成? 我不知道有这种方法,但是通过一些脚本,您可能可以轻松生成所有内容。 如果您使用的是.lib,那么您可能有一个头文件。一点点正则表达式处理,您应该能够提取函数定义,然后使用它们来构造包装函数。听起来像是一个下午的好小项目。还不如同时自动生成.def文件...【参考方案3】:

我按照 anon 的建议做了,我做了一个自动转换器(有人建议在声明之前放置 _ddlspec(export) 并使用此标头编译 dll 就可以了——好吧它没有——也许我做错了什么——我'm Plain Old Java Programmer ;)):

它基本上是解析头文件并转:

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

到:

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

这是代码(很可能是它的 Regexp 滥用,但它有效),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;
        
    

【讨论】:

以上是关于将静态 windows 库转换为 dll的主要内容,如果未能解决你的问题,请参考以下文章

将静态库转换为DLL会导致在main之前发生访问冲突

将静态库转换为 DLL 在 main 之前导致访问冲突

将现有的objective-c静态库转换为swift

如何将 windows 窗体库转换为 windows phone 库

如何从多个静态库中创建一个静态库?

将 Windows 库调用转换为 POSIX 以实现 Linux 兼容性