您的位置:首页 > 编程语言

win 32 dll 编程demo

2015-11-21 21:53 357 查看
话不多说,先贴代码:

test.dll

test.h

#ifdef TEST_EXPORTS
#define TEST_API __declspec(dllexport)
#else
#define TEST_API __declspec(dllimport)
#endif
TEST_API int testadd(int a,int b);


test.cpp

#include "stdafx.h"
#include "test.h"

// 自己写的测试函数
TEST_API int testadd(int a,int b)
{
return a+b;
}


test.def

LIBRARY "test"
EXPORTS
testadd


loadtest.exe

loadtest.cpp

// loadtest.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "windows.h"

int _tmain(int argc, _TCHAR* argv[])
{
printf("hello\n");

**typedef int(*Dllfun)(int ,int);**

HINSTANCE hinst=::LoadLibrary(TEXT("C:\\vsworkstation\\test\\Release\\test.dll"));
if (NULL != hinst)
{
MessageBox(NULL,TEXT("SUCCESS"),NULL,MB_OK);
}
else
{
MessageBox(NULL,TEXT("Error"),TEXT("Error"),MB_OK);
}
int a=1;
int b=2;

***Dllfun msg1;
msg1 = (Dllfun)GetProcAddress(hinst, "testadd");***
if (NULL == msg1)
MessageBox(NULL,TEXT("Error2"),TEXT("Error2"),MB_OK);
else
{
MessageBox(NULL,TEXT("success2"),TEXT("success2"),MB_OK);
int c = msg1(a,b);

printf("jieguoshi   %d",c);
getchar();
}

return 0;
}


vs2008编译

test.dll工程

1、文件——新建——项目,选择win32项目,输入名字test,单击确定,

2、单击下一步

3、选择DLL,勾上导出符号单击完成

4、右击工程,添加——新建项,选择模块定义文件(.def)

5、将上面的对应文件的内容粘进去或敲进去(删掉源文件的示例内容)



注:忽略图中文件名后的2,有2是后来写博客新建的测试工程

loadtest.exe工程

1、文件——新建——项目,选择win32控制台应用程序,输入loadtest,单击确定

2、单击下一步

3、选择控制台应用程序,单击完成

4、将上文loadtest.cpp中的内容粘到loadtest.cpp中

测试截图



附源代码下载地址:这里

参考资料:/article/5925849.html

2015.11.24补充内容:

1、dll和地址空间

dll通常由一组可供任何应用程序或dll使用的独立函数组成,并没有处理消息循环或创建窗口的代码,因此创建dll相对比较简单。

当dll被的文件映像映射到调用进程的地址空间之后,进程中的所有线程就可以调用该dll中的函数了,对进程中的线程来说,该dll中的代码和数据就像是一些附加的代码和数据。当线程调用dll中的一个函数的时候,该函数会在线程栈中取得传给它的参数,并使用线程栈来存放它需要的局部变量。此外,该dll中的函数创建的任何对象都为调用线程或调用进程所拥有的,dll绝对不会拥有任何对象。

地址空间是由一个可执行模块和多个dll模块构成的。

2、dll的构建步骤

(1)必须先创建一个头文件,在其中包含我们想要在dll中导出的函数原型、结构以及符号。为了构建该dll,dll的所有源文件需要包含这个头文件,

(2)创建C/C++源文件来实现想要在dll模块中导出的函数和变量,由于创建该dll的公司尅将这些源代码作为公司的机密。

(3)在构建该dll模块的时候,编译器会对每个源文件进行处理并产生一个.obj模块,

(4)当所有的.obj模块都创建完毕后,链接器会将所有.obj模块的内容合并起来,穿绳一个单独的dll映像文件,。这个映像文件包含dll中所有的二进制代码以及全局/静态变量。为了执行可执行模块,这个文件是必须的。

(5)如果链接器检测到dll的源文件输出了至少一个函数或变量,那么链接器还会生成一个.lib文件。这个文件非常小,因为它不包含任何函数,它只是列出了所有被导出的函数和变量名。

3、构建可执行模块

windows核心编程P513页,这里都不敲了。

4、几点注意

1)避免导出变量,

2)MYLIBAPI在包含头文件之前被定义为_declspec(dllexport)。被MYLIBAPI修饰的变量、函数或C++类会被导出

3)DumpBin.exe(加上-export)可以查看某dll的导出函数,(加上-imports)可以查看一个模块的导入段。

4)让Microsoft编译器不要对导出的函数名进行改编的两种方法,方法一:创建.def文件,包含EXPORTS段,如前文test.def所示。方法二:在dll的源文件中加入一行代码

#pragma comment(linker,"/export:MyFunc=_MyFunc@8")


第二种方法不推荐。

5、两种加载dll的方式

1)隐式加载

延时加载属于隐式加载,需要两个比较重要的链接器开关,/Lib:DelayImp.lib和/delayLoad:MyDll.dll

2)显式加载

显式加载最重要的两个函数 LoadLibrary和GetProcAddress

6、已知dll

一些被系统特殊处理的dll。

当LoadLibrary或LoadLibraryEx被调用的时候,函数首先会检查我们传入的dll的名字是否包含了.dll扩展名,如果没有包含则采用常规的搜索规则来搜索整个dll,如果我们指定了.dll扩展名,那么这两个函数先把扩展名去掉,再在KnownDLLs注册表(HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDlls)中搜索,如果没有找到与之相符的值名,函数会使用正常的搜索规则来搜索。

7、加载程序的搜索dll的顺序

(1)包含可执行文件的目录。

(2)windows的系统目录,该目录可以通过GetSystemDirectory得到,

(3)16位的系统目录,即windows目录中的system子目录,

(4)windows目录,该目录可以通过GetWindowsDirectory得到,

(5)进程的当前目录,

(6)PATH环境变量中所列出的目录,

2015.11.25

昨天下冻雨,冻死了,就提前回去了,今天继续

8、dll重定向

在最初开发windows的时候,内存和磁盘都是非常珍贵的,为了节约的 使用空间及共享资源,Microsoft建议将所有程序的共享模块放到windows系统目录中,是系统能够方便的定位和共享文件。(如C/C++运行库,MFC等)。但是随着时间的推移,安装程序版本更新,使用老版本的文件覆盖这个目录中的文件,从而妨碍了其他程序的正常运行。现在硬盘和内存又大又便宜,因此Microsoft强烈建议开发人员将应用程序文件放到自己的目录中,并且绝对不要碰windows系统目录中的任何东西。

Microsoft从win2k开始新增了一项dll重定向特性,这种特性强制操作系统的加载程序首先从应用程序的目录中载入模块。只有当加载程序无法找到要找的文件时,才会在其他的目录中搜索。

强制加载程序总是先检查应用程序的目录的实现:

1)在应用程序的目录放置一个名为AppName.local的文件

2)创建一个.local的文件夹,并将自己的dll保存在这个文件中

9模块基地址重定位

当我们在构建一个可执行模块的时候,链接器会将模块的首选地址设为0x00400000。对于dll模块来说,链接器会将首选地地址设为0x10000000。这个地址就是模块的首选基地址(表示将模块映射到进程的地址空间的最佳内存地址)。而编译器和链接器在生成机器码的时候,将所有变量的地址固定死了,并被保存在磁盘上的文件映像中。

如果我们的程序有多个dll,都加载在同一个地址0x10000000的话,我们的程序肯定无法运行。因此当连接器在构建我们的模块中的时候,会将重定位段加入嵌入到生成的文件中,当模块无法将模块载入到它的首选地址的时候,系统就会打开重定位段并遍历其中的所有条目。对于每一个条目,加载程序会先找到包含机器指令的哪个存储页面,然后将模块的首选地址与模块的实际映射地址之间的差值,加到机器指令当前的正在使用的内存上。如果这样做会有两个缺点,1是遍历并修改模块中的大量代码,应用程序初始化时间长,2当加载程序写入到模块的代码页面中的时候,系统的写时复制机制会强制这些页面一系统的页交换文件为后备存储器,那么系统不能再抛弃模块的代码页面,并重新载入模块在磁盘上的文件映像。。。。

要是可以在构建完模块后,根据模块的大小自动选定合适的基地址就好了,visual studio提供了一个名为Rebase.exe的工具,在执行Rebase工具的时候,传给他一组映像文件名。它会模拟创建一个进程地址空间,打开应该被载入到这个地址空间的所有模块,并得到每个模块的大小以及他们的首选地址,然后再模拟空间里对模块重定位的过程进行模拟,是各个模块之间没有交叠,修改模块的重定位段,并写入到模块的磁盘文件中,然后更新每个重定位过的文件头。

10模块的绑定
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: