iihero@ChinaUnix, ehero.[iihero] 数据库技术的痴迷爱好者. 您可以通过iihero AT qq.com联系到我 以下是我的三本图书: Sybase ASE in Action, Oracle Spatial及OCI高级编程, Java2网络协议内幕
分类: C/C++
2013-07-18 11:18:24
如若转载,请尊重个人劳动,务必注明原始出处。iihero 2008-9-28于CSDN
前几天,偶然从论坛里看到帖子,有人问:
手头只有oci.dll文件,似乎对应的头文件也有。但是就是找不到对应的lib文件。想让人家邮件发给他一个。
我回忆了一下,好像微软的VC开发工具包里头,有命令可以直接生成对应的lib文件,这样,对于开发人员来说,只要有相应版本的dll文件和头文件,无须对应的lib文件,一样可以隐式加载dll,生成可以执行程序。
其实,现在,想链接某dll,无非两种方式:
1. 编译时通过导入库,进而链接dll,Unix下则通过-l链接指令链接具体的动态库,windows下通过
link /MAP /SUBSYSTEM:CONSOLE /out:
2. 在程序里显式的加载动态库,dlopen/LoadLibrary的API调用可以实现此任务,这时是不需要lib文件的。
当然,对于Unix/Linux平台而言,静态库动态库,都是通过-l指令来编译完成的。只是运行期,如果是动态库,则需要对应的.so,如果是静态库,不需要对应的.a静态库文件。
下面,我就介绍:已知dll文件,如何得到我们第一种方式要用到的导入库lib文件?
这里以PostgreSQL数据库的libpq为例,在%PGHOME%/bin下边有一个动态库文件libpq.dll,它是PostgreSQL客户端要用到的唯一的一个库文件。利用VC带的工具depends,我们看到它里边导出了很多API函数,如下图:
好,我们下边先进入Visual Studio .net 2003 command prompt,这个在你安装的VS的菜单或者对应的目录里边,一般都是一个批处理器文件:VSVars**.bat之类的,我这里就用VC6来演示,效果是一样的:
Microsoft Windows XP [版本 5.1.2600]
(C) 版权所有 1985-2001 Microsoft Corp.
D:/msdev/VC98/Bin>cp d:/pgsql8.3.3/bin/libpq.dll f:/t/
D:/msdev/VC98/Bin>cd /d f:/t/
F:/t>dumpbin /EXPORTS libpq.dll > libpq.txt
F:/t>more libpq.txt
Microsoft (R) COFF Binary File Dumper Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
Dump of file libpq.dll
File Type: DLL
Section contains the following exports for LIBPQ.dll
0 characteristics
484CFEC1 time date stamp Mon Jun 09 17:58:25 2008
0.00 version
1 ordinal base
140 number of functions
140 number of names
ordinal hint RVA name
17 0 000011FE PQbackendPID
36 1 000011DB PQbinaryTuples
122 2 0000111D PQcancel
48 3 00001325 PQclear
72 4 000011C2 PQclientEncoding
F:/t>
我们要注意的一个重要的参数列ordinal列是表示右边的API在dll中的相对序号,这个在后边的def文件里头很重要。
然后使用UltraEdit等带列模式的编辑器,编辑libpq.txt,编辑成文件libpq.def,格式如下:
EXPORTS
PQbackendPID @ 17
PQbinaryTuples @ 36
PQcancel @ 122
PQclear @ 48
PQclientEncoding @ 72
PQcmdStatus @ 42
PQcmdTuples @ 44
PQconndefaults @ 3
PQconnectPoll @ 79
PQconnectStart @ 80
PQconnectdb @ 1
PQconnectionNeedsPassword @ 140
PQconnectionUsedPassword @ 138
PQconninfoFree @ 78
PQconsumeInput @ 26
PQdb @ 7
PQdescribePortal @ 134
PQdescribePrepared @ 133
PQdisplayTuples @ 51
PQdsplen @ 112
PQencryptPassword @ 128
PQendcopy @ 31
PQenv2encoding @ 73
PQerrorMessage @ 15
PQescapeBytea @ 89
PQescapeByteaConn @ 127
PQescapeString @ 88
PQescapeStringConn @ 126
PQexec @ 21
PQexecParams @ 101
PQexecPrepared @ 110
PQfformat @ 109
PQfinish @ 4
PQflush @ 81
PQfmod @ 41
PQfn @ 32
PQfname @ 37
PQfnumber @ 38
PQfreeCancel @ 121
PQfreeNotify @ 87
PQfreemem @ 95
PQfsize @ 40
PQftable @ 107
PQftablecol @ 108
PQftype @ 39
PQgetCancel @ 120
PQgetCopyData @ 105
PQgetResult @ 24
PQgetisnull @ 47
PQgetlength @ 46
PQgetline @ 27
PQgetlineAsync @ 29
PQgetssl @ 114
PQgetvalue @ 45
PQhost @ 10
PQinitSSL @ 124
PQisBusy @ 25
PQisnonblocking @ 82
PQisthreadsafe @ 129
PQmakeEmptyPGresult @ 49
PQmblen @ 64
PQnfields @ 35
PQnotifies @ 22
PQnparams @ 131
PQntuples @ 34
PQoidStatus @ 43
PQoidValue @ 71
PQoptions @ 13
PQparameterStatus @ 97
PQparamtype @ 132
PQpass @ 9
PQport @ 11
PQprepare @ 118
PQprint @ 50
PQprintTuples @ 52
PQprotocolVersion @ 98
PQputCopyData @ 103
PQputCopyEnd @ 104
PQputline @ 28
PQputnbytes @ 30
PQregisterThreadLock @ 125
PQrequestCancel @ 6
PQresStatus @ 66
PQreset @ 5
PQresetPoll @ 83
PQresetStart @ 84
PQresultErrorField @ 106
PQresultErrorMessage @ 65
PQresultStatus @ 33
PQsendDescribePortal @ 136
PQsendDescribePrepared @ 135
PQsendPrepare @ 119
PQsendQuery @ 23
PQsendQueryParams @ 102
PQsendQueryPrepared @ 111
PQserverVersion @ 113
PQsetClientEncoding @ 85
PQsetErrorVerbosity @ 99
PQsetNoticeProcessor @ 20
PQsetNoticeReceiver @ 100
PQsetdbLogin @ 2
PQsetnonblocking @ 86
PQsocket @ 16
PQstatus @ 14
PQtrace @ 18
PQtransactionStatus @ 96
PQtty @ 12
PQunescapeBytea @ 94
PQuntrace @ 19
PQuser @ 8
appendBinaryPQExpBuffer @ 74
appendPQExpBuffer @ 91
appendPQExpBufferChar @ 68
appendPQExpBufferStr @ 75
createPQExpBuffer @ 77
destroyPQExpBuffer @ 76
enlargePQExpBuffer @ 130
initPQExpBuffer @ 69
lo_close @ 54
lo_creat @ 58
lo_create @ 123
lo_export @ 62
lo_import @ 61
lo_lseek @ 57
lo_open @ 53
lo_read @ 55
lo_tell @ 59
lo_truncate @ 137
lo_unlink @ 60
lo_write @ 56
pg_char_to_encoding @ 115
pg_encoding_to_char @ 92
pg_utf_mblen @ 93
pg_valid_server_encoding @ 116
pg_valid_server_encoding_id @ 139
pgresStatus @ 63
pqsignal @ 117
printfPQExpBuffer @ 90
resetPQExpBuffer @ 70
termPQExpBuffer @ 67
接着使用lib命令即可生成对应的导入库及导出文件libpq.lib和libpq.exp,libpq.lib文件你终于可以得到了。
F:/t>lib /def:libpq.def
Microsoft (R) Library Manager Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
LIB : warning LNK4068: /MACHINE not specified; defaulting to IX86
Creating library libpq.lib and object libpq.exp
F:/t>dir libpq.*
驱动器 F 中的卷没有标签。
卷的序列号是 4CDC-CD55
F:/t 的目录
2008-09-28 10:44 6,868 libpq.def
2008-09-28 10:36 167,936 libpq.dll
2008-09-28 10:46 16,299 libpq.exp
2008-09-28 10:46 28,488 libpq.lib
2008-09-28 10:37 6,268 libpq.txt
5 个文件 225,859 字节
0 个目录 1,673,240,576 可用字节
F:/t>
这个文件拿到你的vc6程序当中,直接可以使用。
当然如果是用vc7, vc71, vc8, vc9,那就拿对应命令行环境控制台窗口进去执行上述命令就可以了。
当然,上述方法,一样适用于C++生成的dll。只不过那些导出符号可读性不会那么好了。
最后总结一下,感觉,长期以来,我们习惯了使用微软的Visual Studio IDE,对于普通的命令行使用,反而感到有些茫然,这种现象不太正常,其实,应该像在Unix平台下边一样,学会使用基础的编译命令和makefile的手动编写,那样,你完全可以脱离编译器,使用gvim等编辑器,进行开发,这样反而更能加深对编译链接生成可执行码,有一个比较清晰的思路。
好像新的vc9里头,dll文件中可以有强链接,即,它能指定自己依赖的dll的全路径,那么有些东西是不是发生变化,还有待进一步验证。但是对于传统的dll(相信工业级软件不至于傻到所有dll全用vc9的最新特性来生成),上述方法,依然是一个补救措施。