📄 19.7.2 调用约定.txt
字号:
19.7.2 调用约定
如果这时我们将 Dll2工程中导出函数的调用约定改为标准约定,那么生成的 DLL,其导出函数名字会被改编吗?我们可以试验一下,在 VC++开发环境中重新打开 Dll2工程,然后修改 add和 subtract函数的定义,使它们都采用标准调用约定,结果如例 19-22所示。
例19-22
int _stdcall add(int a , int b) return a+b;
irlt-stdcall subtract (int a , int b)
return a-b;
利用 Build命令生成最新的 Dll2.dll,然后利用 Dumpbin命令的 exports选项查看该 DLL的导出信息,将会发现导出函数的名字仍是 Dll2.def文件中列出的函数名称: add和 subtract,没有发生名字改编。
然后将最新的 Dll2.dll文件复制到 DllTest程序所在目录下。这时应注意,因为该 DLL中导出函数的调用约定发生了改变,那么测试程序 DllTest .中的调用该函数的代码也需要进行相应的改变,即在定义 ADDPROC这一函数指针类型时,也应该定义一个标准调用约定的函数指针类型,否则访问时将会出错。这时, CDllTestDlg类的 OnBtnAdd函数代码如例 19-23所示,其中加灰显示的那条代码就是新修改的代码。
iJtl19-23
void CDllTestDlg::0nBtnAdd()
1. 11 TODO: Add your contro1 notification handler code here
2. HINSTANCE h工 nst;
3. hlnst=LoadLibrary("Dll2.dll");
4. typedef int (_stdca11 *ADDPROC) (int a , int b);
5. ADDPROC Add=(ADDPROC)GetProcAddress(h工 ns t , "add" ) ;
6. i f ( ! Add)
7. {
8. MessageBox ( "获取函数地址失败!");
9. return;
10. }
11. CString str;
12 . str.Format( " 5+3=毛 d" , Add ( 5 , 3 ) ) ;
13 . MessageBox(str);
也就是说,当 DLL中导出函数采用的是标准调用约定时,访问该 DLL的客户端程序也应该采用该约定类型来访问相应的导出函数。
接下来,再分析这样一种情况:如果 DLL导出函数发生了名字改编,那么动态加载 DLL时会发生什么问题?
重新利用 VC++新建一个 Win32 Dynamic-Link Library类型的工程,工程取名为: Dll3 , 并在 AppWizard的第一个步选择" An empty D I1 project"选工页,即创建一个空的动态链接库工程。然后,为该工程添加一个 C++源文件 :Dll3.cpp,为了简单起见,在该源文件中仅编写一个完成加法运算的 add函数,并为其加上_declspec ( dllexport )标识符,表明该函数是导出函数。结果代码如例 19-24所示。
例 19-24
_dec1spec(dllexport) int add(int a.int b)
{
~~~ I 727
第19
return a+b;
利用Build命令生成Dll3.dll,井将该文件复制到DllTest工程所在目录下。
然后,在VC++开发环境中打开DllTest程序,修改OnBtnAdd函数中加载DLL的那行代码(上述例 19-23所示代码中第3行代码),让该函数这时加载Dll3.dll这一动态链接库,然后将其下面的那行代码中的" stdcall"标识符注释起来,修改后的代码如例 19-25所示。
例 19-25
void CDllTestDlg : :OnBtnAdd()
1. 11 TODO : Add your contro1 notification handler code here
2. H工NSTANCE hlnst;
3. hInst=LoadLibrary ("Dll3. dll") ;
4. typedef int (/*_stdca11*1 *ADDPROC) (int a , int b);
5. ADDPROC Add= (ADDPROC) GetProcAddress (hlnst , "add");
6. if( ! Add)
7. {
8 . MessageBox ( "获取函数地址失败!" );
9. return;
10. }
11 . CString str;
12 . str.Format("5+3=屯d" ,Add ( 5 , 3 ) ) ;
13. MessageBox(str);
然后Build并运行DllTest程序,当程序界面显示之后,单击其中的【Add】按钮,这时将弹出一个消息框,提示:"获取函数地址失败!"。这是因为在生成Dll3.dll程序时,导出函数 add的名称会发生名字改编。可以利用Dumpbin命令的 exports选项查看Dll3.dll的导出信息,结果如图 19.20所示。可以看到,这时导出函数 add的名称变为: "?add@ @YAI-llIH@Z飞因此,这时在DllTest程序中访问Dll3.dll的导出函数: add时,就找不到该函数。
图 19.20 Dll3.dll的导出信息
728 I ~如协
为了验证 "?add@@YAHHH@Z"就是 Dll3 .dll中导出函数 add的名字,我们可以修改 DllTest程序中获得 add函数地址的那行代码,即让 GetProcAddress函数直接获得 "?add@@YAHHH@Z"这一函数的地址,修改后的代码如例 19-26所示。
例 19-26
void CDIITestDlg: :OnBtnAdd()
1. // TODO: Add your control notification handler code here
2. HINSTANCE h工 nst;
3. hlnst=LoadL工 brary ("0113 . dll") ;
4. typedef int (/ *_stdca11* / *ADDPROC) (int a , int b);
5. AODPROC Add=(ADOPROC)GetProcAddress(h工 nst," ?add@@YAHHH@Z");
6. i f ( ! Add)
7. {
8. MessageBox ( "获取函数地址失败!");
9. return;
10. }
11. CString str;
12 . str . Format("5+3 =毛 d " , Add ( 5 , 3 ) ) ;
13 . MessageBox(str);
Build并运行 Dl1Test程序,之后单击【 Add】按钮,可以发现调用成功。说明 "?add@@YAHHH@Z"确实是 Dll3 .dll中导出函数 add的名字。
.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -