從DLL產生LIB
cheungmine
2013-5-14
windows程式連結到一個動態連結程式庫.dll時需要一個匯入庫.lib。遺憾的是這樣的匯入庫很多時候是不存在的,那麼就需要我們自己從.dll產生對應的匯入庫.lib。假設我們的windows程式為WinApp.exe,它靜態連結到一個動態庫libABC.dll。我們在WinApp.exe的原始碼中可以這樣寫:
// (程式清單1)// WinApp.c//#include <windows.h>#include "C:/DEVPACK/LIBABC/include/ABCapi.h"# pragma comment(lib, "C:/DEVPACK/LIBABC/lib/libABC.lib");int main(){ // 下面可以直接使用 LIBABC 的函數 ABCInit(); ABCAddValues(a, b); ABCUninit(); exit(0);}
但是問題是,我們手裡只有libABC.dll和標頭檔。沒有libABC.lib。這就需要我們從命令列工具產生出libABC.lib。從DLL產生LIB的過程是:DLL->DEF->LIB。需要產生一個叫做模組定義檔案(.def)的中間檔案。一個典型的模組定義檔案(libABC.def)如下(程式清單2):
LIBRARY libABCEXPORTS ABCInit ABCUninit ABCAddValues ...
windows的vs系列開發工具提供了這個命令列dumpbin,它的調用方式是開啟cmd,然後輸入下面的命令:
dumpbin /EXPORTS libABC.dll > libABC.def
上面的命令執行後產生的libABC.def檔案包含了對於產生LIB無用的資訊,我們必須手工處理掉,然後才能帶入下面的命令中產生LIB。假設我們已經把libABC.def處理成(程式清單2)的樣子。接下來使用另外一個命令列工具,通過這個def檔案產生LIB:
lib /def:libABC.def /machine:i386 /out:libABC.dll
總結下來,整個過程有3步:
1)DLL->def
2)處理def
3)使用處理後的def->LIB
整個過程在第2步不是自動化的,我曾經寫過一個指令碼完成第2步的自動化。後來發現MinGW提供了這樣的工具:pexports。預設的MinGW沒有安裝pexports,你需要運行下面的命令在MinGW中安裝:
$ mingw-get install pexports
於是,產生處理好的def的工作就變得非常簡單:
$ pexports libABC.dll > libABC.def
無論是dumpbin還是lib這2個命令都需要VS的運行環境。因此必須先設定環境變數,通過運行一個在%VS??COMNTOOLS%的路徑下的批次檔vsvars32.bat(??表示版本號碼,可以查看windows環境變數得到這個資訊),來完成VS環境變數設定。過程就是開啟windows cmd,輸入下面的命令:
%VS100COMNTOOLS%vsvars32.bat
最後我把整個處理過程寫成了python指令碼,這個指令碼執行起來特別簡單,開啟MinGW命令列,進入libABC.dll所在的目錄,輸入下面的命令:
$ python mklib-win32.py libABC或者$ python mklib-win32.py libABC.dll
最後,我把mklib-win32.py貼出來,以饗讀者:
#!/usr/bin/python# filename: mklib-win32.py# make import x86_32 import-lib from windows win32 dll# author: cheungmine@gmail.com# date: 2013-5# version: 0.1## MinGW:# $ python build-win32.py target_dll# $ python build-win32.py libtiff-5.dll# $ python build-win32.py libtiff-5# $ python build-win32.py tiff-5# $ python build-win32.py c:/path/to/libtiff-5# ERROR: $ python build-win32.py c:\path\to\libtiff-5## file operation:# import shutil### copy file:# shutil.copy(myfile, tmpfile)### copy time of file:# shutil.copy2(myfile, tmpfile)### copy file dir tree, the 3rd parameter means:## True: symbol link## False: use phyical copy# shutil.copytree(root_of_tree, destination_dir, True)###############################################################################import osimport platformimport timeimport getoptimport optparseimport sysimport string################################################################################ get installed VS???COMNTOOLS environment:###############################################################################def get_vspath(): _vspath = os.getenv('VS110COMNTOOLS') if not _vspath: _vspath = os.getenv('VS100COMNTOOLS') if not _vspath: _vspath = os.getenv('VS90COMNTOOLS') if not _vspath: _vspath = os.getenv('VS80COMNTOOLS') if not _vspath: print "VS??COMNTOOLS not found" sys.exit() else: print "VS80COMNTOOLS =", _vspath else: print "VS90COMNTOOLS =", _vspath else: print "VS100COMNTOOLS =", _vspath else: print "VS110COMNTOOLS =", _vspath return _vspath################################################################################ step (1): create a windows module definition: target_lib.def# MSCMD:# > dumpbin /EXPORTS target_lib.dll > ~target_lib.def# or MinGW:# $ pexports target_lib.dll > target_lib.def# step (2): use this target_lib.def to create module import file: target_lib.lib# MSCMD:# > lib /def:target_lib.def /machine:i386 /out:target_lib.lib###############################################################################def make_lib(workdir, tgtname): print "[2-1] create a windows module definition: lib%s.def" % tgtname dump_def = 'cd "%s"&pexports lib%s.dll > lib%s.def' % \ (work_dir, tgtname, tgtname) ret = os.system(dump_def) if ret == 0: print "[2-2] use (lib%s.def) to create import module: lib%s.lib" % (tgtname, tgtname) lib_cmd = 'cd "%s"&lib /def:lib%s.def /machine:i386 /out:lib%s.lib' % (workdir, tgtname, tgtname) cmds = 'cd "%s"&vsvars32.bat&%s&cd "%s"' % (vs_path, lib_cmd, cwd_path) ret = os.system(cmds) if ret == 0: print "INFO: mklib (%s/lib%s.lib) success." % (cwd_path, tgtname) return 0; else: print "ERROR: mklib (%s/lib%s.lib) failed." % (cwd_path, tgtname) return (-2) else: print "ERROR: mklib (%s/lib%s.def) failed." % (cwd_path, tgtname) return (-1);################################################################################ current directory:cwd_path = os.getcwd()vs_path = get_vspath()work_dir = "./"# lib name == parent folder nametarget_dll = "ERROR_dll_not_found"if sys.argv.__len__() == 1: work_dir, target_dll = os.path.split(cwd_path)elif sys.argv.__len__() == 2: work_dir = os.path.dirname(sys.argv[1]) target_dll = os.path.basename(sys.argv[1])else: print "ERROR: invalid argument" sys.exit(-1)if target_dll[0:3] == "lib": target_dll = target_dll[3:]tgtname, extname = os.path.splitext(target_dll)if extname != ".dll": tgtname = target_dllif work_dir == "": work_dir = cwd_pathprint "working directory:", work_dirprint "======== make import (lib%s.lib) from (lib%s.dll) ========" % \ (tgtname, tgtname)make_lib(work_dir, tgtname)sys.exit(0)
由於近期開發windows x64程式,因此需要產生64位的DLL對應的LIB,於是我又在mklib-win32.py的基礎上完成了mklib-win64.py的指令碼,具體原理我就不說了,讀者注意指令碼之中的細微之處不難理解:
#!/usr/bin/python# filename: mklib-win64.py# make import x86_64 import-lib from windows x64 dll# author: cheungmine@gmail.com# date: 2013-5# version: 0.1## MinGW:# $ python build-win64.py target_dll# $ python build-win64.py libtiff-5.dll# $ python build-win64.py libtiff-5# $ python build-win64.py tiff-5# $ python build-win64.py c:/path/to/libtiff-5# ERROR: $ python build-win64.py c:\path\to\libtiff-5## file operation:# import shutil### copy file:# shutil.copy(myfile, tmpfile)### copy time of file:# shutil.copy2(myfile, tmpfile)### copy file dir tree, the 3rd parameter means:## True: symbol link## False: use phyical copy# shutil.copytree(root_of_tree, destination_dir, True)###############################################################################import osimport platformimport timeimport getoptimport optparseimport sysimport string################################################################################ get installed VS???COMNTOOLS environment:###############################################################################def get_vspath(): _vspath = os.getenv('VS110COMNTOOLS') if not _vspath: _vspath = os.getenv('VS100COMNTOOLS') if not _vspath: _vspath = os.getenv('VS90COMNTOOLS') if not _vspath: _vspath = os.getenv('VS80COMNTOOLS') if not _vspath: print "VS??COMNTOOLS not found" sys.exit() else: print "VS80COMNTOOLS =", _vspath else: print "VS90COMNTOOLS =", _vspath else: print "VS100COMNTOOLS =", _vspath else: print "VS110COMNTOOLS =", _vspath return _vspath################################################################################ step (1): create a windows module definition: target_lib.def# MSCMD:# > dumpbin /EXPORTS target_lib.dll > ~target_lib.def# or MinGW:# $ pexports target_lib.dll > target_lib.def# step (2): use this target_lib.def to create module import file: target_lib.lib# MSCMD:# > lib /def:target_lib.def /machine:amd64 /out:target_lib.lib###############################################################################def make_lib(workdir, tgtname): print "[2-1] create a windows module definition: lib%s.def" % tgtname dump_def = 'cd "%s"&pexports lib%s.dll > lib%s.def' % \ (work_dir, tgtname, tgtname) ret = os.system(dump_def) if ret == 0: print "[2-2] use (lib%s.def) to create import module: lib%s.lib" % (tgtname, tgtname) lib_cmd = 'cd "%s"&lib /def:lib%s.def /machine:amd64 /out:lib%s.lib' % (workdir, tgtname, tgtname) cmds = 'cd "%s"&vcvarsall.bat x86_amd64&%s&cd "%s"' % (vs_path, lib_cmd, cwd_path) ret = os.system(cmds) if ret == 0: print "INFO: mklib (%s/lib%s.lib) success." % (cwd_path, tgtname) return 0; else: print "ERROR: mklib (%s/lib%s.lib) failed." % (cwd_path, tgtname) return (-2) else: print "ERROR: mklib (%s/lib%s.def) failed." % (cwd_path, tgtname) return (-1);################################################################################ current directory:cwd_path = os.getcwd()vs_path = get_vspath() + "..\\..\\VC\\"work_dir = "./"# lib name == parent folder nametarget_dll = "ERROR_dll_not_found"if sys.argv.__len__() == 1: work_dir, target_dll = os.path.split(cwd_path)elif sys.argv.__len__() == 2: work_dir = os.path.dirname(sys.argv[1]) target_dll = os.path.basename(sys.argv[1])else: print "ERROR: invalid argument" sys.exit(-1)if target_dll[0:3] == "lib": target_dll = target_dll[3:]tgtname, extname = os.path.splitext(target_dll)if extname != ".dll": tgtname = target_dllif work_dir == "": work_dir = cwd_pathprint "working directory:", work_dirprint "======== make import (lib%s.lib) from (lib%s.dll) ========" % \ (tgtname, tgtname)make_lib(work_dir, tgtname)sys.exit(0)
我做了個實驗,就是用MinGW構建sqlite3,預設沒有構建出我需要的dll,於是我首先進入sqlite3.o所在的目錄,運行下面的命令手工構建出dll:
$ gcc -shared -fPIC sqlite3.o -o libsqlite3.dll -s
然後用mklib-win64.py產生lib檔案:
$ python mklib-win64.py libsqlite3.dll
最後產生了64位版本的libsqlite3.lib,通過VS2010編寫的程式測試,完全正確。
#include "C:/DEVPACK/MinGW/local64/dst/sqlite3/include/sqlite3.h"# pragma comment(lib, "C:/DEVPACK/MinGW/local64/dst/sqlite3/lib/libsqlite3.lib");......void test_sqlite3(){ int ret; sqlite3 *dbconn; sqlite3_stmt *stmt; char *errmsg; ret = sqlite3_open_v2("C:/workspace/antelope/test.db", &dbconn, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_SHAREDCACHE, 0); if (ret != SQLITE_OK) { fprintf(stdout, "ERROR: sqlite3_open_v2() error (%d).\n", ret); }}
於是,便有了此文!