社区
进程/线程/DLL
帖子详情
如何使自己开发的dll让Depends工具看不到导出的函数名?
BillShow
2005-02-27 04:56:44
我记得以前看到过相应的文章,但是不记得了.请大家教教我.
...全文
198
9
打赏
收藏
如何使自己开发的dll让Depends工具看不到导出的函数名?
我记得以前看到过相应的文章,但是不记得了.请大家教教我.
复制链接
扫一扫
分享
转发到动态
举报
写回复
配置赞助广告
用AI写文章
9 条
回复
切换为时间正序
请发表友善的回复…
发表回复
打赏红包
fbmsf
2005-03-02
打赏
举报
回复
喔,看错了,只用序列号就可以了。就是def 文件。里面不要函数名,
具体看msdn .link
eunt
2005-02-28
打赏
举报
回复
好像名字到一定长度就行了
BillShow
2005-02-28
打赏
举报
回复
LoadLibrary,GetProcAddress是在调用Dll里面的函数采用的吧?
我要做的是如何让我编写的Dll,出于保密的原因,让其他人无法看到我DLL里面导出了哪些函数,只有看过我Dll文档的才知道.
fbmsf
2005-02-28
打赏
举报
回复
这个问题很容易,就是你的函数全部用动态调入的形式。
就是LoadLibrary,GetProcAddress
这在病毒的编写中很常见。
BillShow
2005-02-27
打赏
举报
回复
具体如何做了?
cnwolf
2005-02-27
打赏
举报
回复
不会 吧!?
dll是pe格式的,怎么也有让别的程序调用的导出表吧
Depends不是读的这个?
QunKangLi
2005-02-27
打赏
举报
回复
只用序号导出?
fanqing
2005-02-27
打赏
举报
回复
学习+关注
BillShow
2005-02-27
打赏
举报
回复
顺便说一句,我用def文件导出函数的.
自己写的
dll
的简介
为何
DLL
先看看静态库与
DLL
的不同之处 可执行文件的生成(Link期):前者很慢(因为要将库中的所有符号定义Link到EXE文件中),而后者很快(因为后者被Link的引入库文件无符号定义) 可执行文件的大小:前者很大,后者很小(加上
DLL
的大小就和前者差不多了) 可执行文件的运行速度:前者快(直接在EXE模块的内存中查找符号),后者慢(需要在
DLL
模块的内存中查找,在另一个模块的内存中查找自然较慢) 可共享性:前者不可共享,也就是说如果两个EXE使用了同一个静态库,那么实际在内存中存在此库的两份拷贝,而后者是可共享的。 可升级性:前者不可升级(因为静态库符号已经编入EXE中,要升级则EXE也需要重新编译),后者可以升级(只要接口不变,
DLL
即可被升级为不同的实现) 综合以上,选择静态库还是
DLL
1. 静态库适于稳定的代码,而动态库则适于经常更改代码(当然接口要保持不变),当
DLL
更改(仅实现部分)后,用户不需要重编工程,只需要使用新的
Dll
即可。 2. 由于静态库很吃可执行文件的生成(Link期)时间,所以如果对可执行文件的Link时间比较敏感,那么就用
DLL
。 使用
DLL
在介绍如何创建
DLL
之前,让我们先了解它是如何被使用的。 1. 显式调用(也叫动态调用) 显 示调用使用API
函数
LoadLibrary或者MFC提供的AfxLoadLibrary将
DLL
加载到内存,再用GetProcAddress()在 内存中获取引入
函数
地址,然后你就可以象使用本地
函数
一样来调用此引入
函数
了。在应用程序退出之前,应该用FreeLibrary或MFC提供的 AfxLoadLibrary释放
DLL
。 下面是个显示调用的例子,假定你已经有一个Test.
dll
,并且
DLL
中有个
函数
名
为Test,其声明式是void(); #include < iostream > using namespace std; typedef void(*TEST )(); int main( char argc, char* argv[] ) { const char*
dll
Name = "Test.
dll
"; const char* funcName = "Test"; HMODULE h
DLL
= LoadLibrary(
dll
Name ); if ( h
DLL
!= NULL ) { TEST func = TEST( GetProcAddress( h
DLL
, funcName ) ); if ( func != NULL ) { func(); } else { cout << "Unable to find function \'" << funcName << "\' !" << endl; } FreeLibrary( h
DLL
); } else { cout << "Unable to load
DLL
\'" <<
dll
Name << "\' !" << endl; } return 0; } 注意 1. 显示调用使用GetProcAddress,所以只能加载
函数
,无法加载变量和类。 2. 此外GetProcAddress是直接在.
dll
文件中寻找同
名
函数
,如果
DLL
中的Test
函数
是个C++
函数
,那么由于在.
dll
文件中的实际文件
名
会被修饰(具体被修饰的规则可参考
函数
调用约定详解或者使用VC自带的
Depends
.exe查看),所以直接找Test是找不到的,这时必须把
函数
名
修改为正确的被修饰后的
名
称,下面是一种可能(此
函数
在
DLL
中的调用约定为__cdecl): const char* funcName = "?Test@@YAXXZ"; 2. 隐式调用(也叫静态调用) 隐式调用必须提供
DLL
的头文件和引入库(可以看作轻量级的静态库(没有符号定义,但是说明了符号处于哪个
DLL
中))。 有了头文件和引入库,
DLL
的使用就跟普通静态库的使用没啥区别,只除了
DLL
要和EXE一起发布。 显示调用与隐式调用的优缺点 显示调用使用复杂,但能更加有效地使用内存,因为
DLL
是在EXE运行时(run time)加载,必须由用户使用LoadLibrary和FreeLibrary来控制
DLL
从内存加载或卸载的时机。此外还可以加载其他语言编写的
DLL
函数
。 静态调用使用简单,但不能控制
DLL
加载时机,EXE加载到内存同时自动加载
DLL
到内存,EXE退出时
DLL
也被卸载。 创建
DLL
下面我们着重讲解如何在VC下创建
DLL
首先要建立一个Win32的
DLL
工程。再把普通静态库的所有文件转移到
DLL
工程中,然后: 为所有的类声明加上__declspec(
dll
export)关键字,这有这样编译器才能自动为你产生引入库文件(否则你需要自己写.def文件来产生,因为不常用所以在此不再阐述其细节) 对于
DLL
的用户来讲,类声明就需要用另外一个关键字__declspec(
dll
import)(此关键字对于类和
函数
而言并非必须,但对于变量而言则是必须的)。所以通常我们会定义一个宏来包装之,比如 #ifdef MY
DLL
_EXPORTS # define MY
DLL
_API __declspec(
dll
export) #else # define MY
DLL
_API __declspec(
dll
import) #endif 这样我们就能写出如下的类 class MY
DLL
_API MyClass { ... }; 当然在创建
DLL
的工程里需要包含preprocessor(预处理指示器)MY
DLL
_EXPORTS,而在用户工程里面则不应该包含MY
DLL
_EXPORTS。 其实上面说的VC早就帮我们做好了。如果你创建的
DLL
工程叫做Test,那么此工程自动包含TEST_EXPORTS。如果你在创建工程的时候选择了Exprot Symbols, 那么VC还会自动帮你创建一个示例文件Test.h,此文件定义出 #ifdef TEST_EXPORTS # define TEST_API __declspec(
dll
export) #else # define TEST_API __declspec(
dll
import) #endif 你自定义的文件都应该包含此文件以使用TEST_API。(如果没有选择Exprot Symbols,那么就得自己动手写出这段宏了) 示例文件中还包括了一个类,变量,以及全局
函数
的写法 class TEST_API CTest { public: CTest(void); // TODO: add your methods here. }; extern TEST_API int nTest; TEST_API int fnTest(void); 通过上面的示例我们也可以看出全局(或者
名
字空间)变量和
函数
的声明方法 细节讨论 1.
DLL
的入口
函数
Dll
Main并非必须,如果没有它,编译器会帮你自动生成一个不做任何事的
Dll
Main。 2. 如果是可以定义在头文件里面的东西:包括宏,常量,内联
函数
(包括成员内联
函数
)以及模板。那么在
DLL
中的定义中可以不必包含TEST_API,和普通的定义没有区别。 如果一个类完全由内联
函数
和纯虚
函数
构成,那么也不需要TEST_API这样的关键字。一个不带成员
函数
的struct也一样。 3. 所有未经__declspec(
dll
export)
导出
的类都只能作为
DLL
内部类使用。当然外部仍然可以使用其内联成员
函数
(前提是该成员
函数
不应该调用任何未经
导出
的
函数
或者类成员
函数
) 发布
DLL
1. 程序库的作者应该将三件套:头文件,引入库文件和
DLL
一同发布给用户,其中头文件和引入库是专为静态调用的用户准备,也就是C/C++用户。(此外有些
DLL
内部使用的头文件,如果没有被接口头文件直接#include,那么该头文件就不必发布,这和静态库是一样的)。 2.
DLL
的用户只需将
DLL
和可执行程序一同发布即可。 C++程序使用C语言
DLL
(静态库规则一样) C不存在class, 所以由C创建的
DLL
必然也不会出现class;对于全局变量的使用C和C++没有什么不同,所以我们把焦点集中在全局
函数
上(此外全局变量的规则一样)。 我们知道C++程序要使用C语言的
函数
就必须在
函数
声明前加上extern "C"关键字,这对于静态库和
DLL
没有什么不同。 但是这个关键字不能直接出现在头文件
函数
声明中,否则
DLL
无法通过编译, 原因很简单,C语言并没有extern "C"这个关键字。 1. 一种作法是把C向C++迁移的责任交给
DLL
创建者。定义出一个宏,以使
DLL
(C工程)中不出现extern "C"或者只是extern,而在用户工程(C++工程)中保持原样。幸运的是windows早已为我们设计好一切,这个宏就是EXTERN_C(存在于 winnt.h中) : #ifdef __cplusplus #define EXTERN_C extern "C" #else #define EXTERN_C extern #endif 注意上面必须是extern而不是空。因为虽然C的
函数
声明不是必须添加extern,但是变量声明则必须添加extern。 有了EXTERN_C,在头文件中这样定义
函数
: EXTERN_C TEST_API int fnTest(void); 2. 另外一种做法是把把C向C++迁移的责任交给用户,用户在包含
DLL
头文件的时候以extern "C"来include: extern "C" { #include "my
dll
.h" #include "my
dll
core.h" ... } 可以把上述的extern "C"段放在新的头文件中和
DLL
一起发布,这样C++用户只需要包含这个头文件就可以了。比如Lua库就自带了一个etc/lua.hpp文件。通常此文件会由
DLL
作者提供,所以此时迁移的责任仍在
DLL
创建者。 注意用户不要试图以extern "C"来重新声明
函数
,因为重复声明是允许的,但是必须保证和头文件中的声明相同。 3. 这种做法的一个变形就是直接在C头文件中以extern "C"将所有
函数
和变量声明包含之,这样就无需像上面那样多提供一个额外的头文件了。通常像这样(my
dll
头文件): #include 头文件段 #ifdef __cplusplus extern "C" { #endif
函数
和变量声明 ... #ifdef __cplusplus } #endif 创建标准的
DLL
,使其可被其他语言使用 通常我们会希望
DLL
能够被其他语言使用,因而我们的
DLL
往往不会提供类定义,而只提供
函数
定义。(因为许多编程语言是不支持类的)。 此时
函数
的调用约定必须是__stdcall(在vc下默认是__cdecl,所以你不得不手工添置),因为大部分语言不支持__cdecl,但支持__stdcall(比如VBScript,Delphi等)。 此外我们希望
导出
到
DLL
中的
函数
名
能够更易被识别(用户使用才会更方便),也就是说
DLL
应该编译出无修饰的C
函数
名
。 所以我们可能会 1. 如果你只用C语言,那么必然以C文件创建
DLL
(自动编出C符号
名
),考虑到潜在的C++用户(此类用户多以静态调用方式使用
DLL
,因而需要看到其
函数
声明),我们还需要使用EXTERN_C关键字(详见上面的讨论)。 2. 如果你使用C++,那么必然以C++文件创建
DLL
,这时你应该直接以extern "C"修饰。 结论 所以要创建能够为任意编程语言使用之
DLL
,我们应该 1. 只创建
函数
2. 声明__stdcall调用约定(或者WINAPI,CALLBACK,前提是你必须包含windows头文件) 3. 以CPP文件 + extern "C" 或者 C文件 + EXTERN_C 4. 当然还需要
DLL
_API的声明(如果光有
dll
export,那么
DLL
也只能被显示调用)。 更完美一点 不应该使用
dll
export和extern "C"而应该使用.def文件。虽然经过上面的4个步骤,我们的
DLL
里面的C
函数
名
已经相当简洁了,但仍不是最完美的:比如一个声明为function的
函数
,实际在
DLL
中的
名
字确可能是function@#。而使用.def文件,我们可以让
DLL
中的
函数
名
和声明的
函数
名
保持一致。 关于.def的详细用法可参考: 微软
DLL
专题 使用 DEF 文件从
DLL
导出
在
DLL
与静态库之间切换 前面我曾经提到对于使用
DLL
的用户__declspec(
dll
import)关键字可有可无,当然前提是
DLL
中不包括变量定义。 所以要把库既能够做成
DLL
,也能够做成静态库,那么就应该作出类似下面这样的定义: 1.
DLL
不包括变量的定义 #ifdef TEST_EXPORTS # define TEST_API __declspec(
dll
export) #else # define TEST_API #endif 然 后只要把工程的配置属性(Configuration Type)简单地在Static Library (.lib)或者Dynamic Library (.
dll
)切换即可(VC会自动地为
DLL
添加预处理器TEST_EXPORTS,为静态库取消TEST_EXPORTS)。 2.
DLL
包含变量定义,也是标准的做法 #ifdef TEST_BUILD_AS_
DLL
#ifdef TEST_EXPORTS # define TEST_API __declspec(
dll
export) #else # define TEST_API __declspec(
dll
import) #endif #else #define TEST_API #endif 如果要将库做成
DLL
,那么需要
DLL
创建者添加预处理器TEST_BUILD_AS_
DLL
和TEST_EXPORTS,后者通常由编译器自动添加;如果做成静态库则不需要添加任何预处理器。 用户则可以通过添加或取消TEST_BUILD_AS_
DLL
来使用动态库或静态库。 对于
DLL
的用户,TEST_BUILD_AS_
DLL
这个
名
称似乎起得不好。下面的做法或许会更合理些: #if defined(TEST_API) #error "macro alreday defined!" #endif #if defined(TEST_BUILD_
DLL
) && defined(TEST_USE_
DLL
) #error "macro conflict!" #endif #if defined(TEST_BUILD_
DLL
) // build
dll
# define TEST_API __declspec(
dll
export) #elif defined(TEST_USE_
DLL
) // use
dll
# define TEST_API __declspec(
dll
import) #else // build or use library, no preprocessor needs # define TEST_API #endif 相关链接 微软
DLL
专题 Windows API编程之动态链接库(
DLL
)
DLL
(动态库)
导出
函数
名
乱码含义
C++编译时
函数
名
修饰约定规则: __stdcall调用约定: 1、以"?"标识
函数
名
的开始,后跟
函数
名
; 2、
函数
名
后面以"@@YG"标识参数表的开始,后跟参数表; 3、参数表以代号表示: X--void D--char E--unsigned char F--short H--in
VC++中
Depends
工具
的使用和
DLL
函数
的动态调用方式示例
VC++中
Depends
工具
;
Depends
用来显示与一个可执行文件(exe或者
dll
)相关的依赖项(动态链接库
dll
),以及该exe或
dll
引用了这些
dll
中的哪些接口,同时也可以看到每个被依赖的
dll
中的所有
导出
的
函数
接口。 在VC6位于如下目录; 加载一个系统
dll
查看一下;出现下图错误提示;大致说...加载失败...循环依赖...;因为此
dll
又调用了其它
dll
; 左上角是D...
dll
导出
函数
名
的那些事
dll
导出
函数
名
的那些事 关键字: VC++
DLL
导出
函数
经常使用VC6的Dependency或者是
Depends
工具
查看
DLL
导出
函数
的
名
字,会发现有
DLL
导出
函数
的
名
字有时大不相同,导致不同的原因大多是和编译
DLL
时候指定
DLL
导出
函数
的界定符有关系。 VC++支持两种语言:即C/C++,这也是造成
DLL
导出
函数
差异的根源 我们用VS2008新建个
DLL
工程,...
DLL
导出
函数
名
称改编的解决方法
1.
DLL
编译后
导出
函数
名
称改编 在编写一个
DLL
后,为了能被别的程序调用,需要将被使用的
函数
导出
; 但是一般的编译器都会将到处
函数
名
称改编; 例如:在VC中新建一个空的win32
dll
工程,然后添加下面的文件; [cpp] view plaincopy //
dll
1.h #ifdef
DLL
_API _dec
进程/线程/DLL
15,471
社区成员
49,182
社区内容
发帖
与我相关
我的任务
进程/线程/DLL
VC/MFC 进程/线程/DLL
复制链接
扫一扫
分享
社区描述
VC/MFC 进程/线程/DLL
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
暂无公告
试试用AI创作助手写篇文章吧
+ 用AI写文章