DLL(二)

一 建立工程dll1

工程就一个dll1.cpp文件,代码如下:

_declspec(dllexport)  int  add( int  a, int  b)
{
    
return  a + b;
}

_declspec(dllexport) 
int  subtract( int  a, int  b)
{
    
return  a - b;
}

 

会在dubeg目录下生成dll1.lib和dll1.dll两个主要文件

 

二 建立测试程序dllTest

 

extern   int  add( int , int );
extern   int  subtract( int , int );

void  CdllTestDlg::OnBnClickedButton1()
{
    
//  TODO: 在此添加控件通知处理程序代码
    CString str;
    str.Format(_T(
" 5+3=%d " ),add( 5 , 3 ));
    MessageBox(str);
}

void  CdllTestDlg::OnBnClickedButton2()
{
    
//  TODO: 在此添加控件通知处理程序代码
    CString str;
    str.Format(_T(
" 5-3=%d " ),subtract( 5 , 3 ));
    MessageBox(str);
}

 

编译时出错:

error LNK2019: 无法解析的外部符号 "int __cdecl add(int,int)" (?add@@YAHHH@Z),该符号在函数 "public: void __thiscall CdllTestDlg::OnBnClickedButton1(void)" (?OnBnClickedButton1@CdllTestDlg@@QAEXXZ) 中被引用

error LNK2019: 无法解析的外部符号 "int __cdecl subtract(int,int)" (?subtract@@YAHHH@Z),该符号在函数 "public: void __thiscall CdllTestDlg::OnBnClickedButton2(void)" (?OnBnClickedButton2@CdllTestDlg@@QAEXXZ) 中被引用

解决办法:

连接的时候要找到这两个函数的实现

将用到的dll1.dll的引入库文件dll1.lib文件拷到当前工程的目录下

#pragma  comment(lib,"dll1.lib")

上面的代码在编译的时候是没有问题的,输出如下:

1>------ 已启动生成: 项目: dllTest, 配置: Debug Win32 ------
1>正在编译...
1>正在跳过...(未检测到相关更改)
1>dllTestDlg.cpp
1>生成日志保存在“file://e:\vs2005项目\dllTest\dllTest\Debug\BuildLog.htm”
1>dllTest - 0 个错误,0 个警告
========== 生成: 1 已成功, 0 已失败, 0 最新, 0 已跳过 ==========

但是在运行的时候还是出错了,提示没有找到dll1.dll。我们发现在编译时不会出错,但是运行时出错,这是因为DLL是运行时才加载到进程空间的,所以是动态链接库。那就把dll1.dll加到当前工程的目录下就可以

DLL的搜寻路径:

1)进程的当前目录

2)Windows目录的系统目录

3)Windows目录

4)PATH环境变量中列出的各个目录

 

我们之前是用extern来声明DLL中的这些函数是在外部可以找到,我们可以直接告诉编译器这些是在dll的lib文件中找到。

 

 

// 从外部引入
// extern int add(int,int);
// extern int subtract(int,int);

// 告诉编译器我们是从.lib文件引入
// 注意:这里是_declspec(dllimport)而不是_declspec(dllexport)是引入而不是导出
_declspec(dllimport)  int  add( int , int );
_declspec(dllimport)  
int  subtract( int , int );

void  CdllTestDlg::OnBnClickedButton1()
{
    
//  TODO: 在此添加控件通知处理程序代码
    CString str;
    str.Format(_T(
" 5+3=%d " ),add( 5 , 3 ));
    MessageBox(str);
}

void  CdllTestDlg::OnBnClickedButton2()
{
    
//  TODO: 在此添加控件通知处理程序代码
    CString str;
    str.Format(_T(
" 5-3=%d " ),subtract( 5 , 3 ));
    MessageBox(str);
}

我们发现这样在代码中这样的声明是很杂乱的,不利于管理,我们希望把这些导出函数的声明都由DLL的制作者来提高,因为本来DLL提供哪些导出函数

也只有DLL的制作者才知道和了解,那么就应该由DLL的制作者来提供一个.h文件

/*
  dll1.h(v1.0)声明这些函数是DLL的导出函数
*/
_declspec(dllimport) 
int  add( int  a, int  b);
_declspec(dllimport) 
int  subtract( int  a, int  b);

 

这样我们的用户只需要

包含这个头文件就可以直接使用了

#include  " ..\dll1\dll1.h " //返回当前目录的上一层目录的dll1文件夹下的dll1.h

现在我们想这个头文件同时包含在dll1.cpp和dllTest.cpp里面我们怎么办,要实现的功能显然是不一样的,我们想到了条件编译。

/*
 dll1.h(v2.0)
*/
#ifdef DLL1_API
#else
#define  DLL1_API  _declspec(dllimport)
#endif

DLL1_API 
int   add( int  a, int  b);   
DLL1_API 
int   subtract( int  a, int  b);

在dll1.cpp下面这样来做

/*
   dll1.cpp
*/
#define  DLL1_API  _declspec(dllexport)
#include 
" Dll1.h "

int  add( int  a, int  b)
{
    
return  a + b;
}

int  subtract( int  a, int  b)
{
    
return  a - b;
}

 在DLL的创建者这边,先定义了DLL1_API为导出函数,如果不在dll1.h之前这么定义,那么实际上就被dll1.h定义成导入函数,而我们

DLL的使用者就不需要定义这个宏,直接包含了就可以了。

 

我们不但是只想用dll中的全局函数,还想用dll中的类,我们怎么办。

class  DLL1_API Point
{
public :
    
void  output( int  x, int  y);
    
void  test();
};

这样这个类就可以被dllTest.cpp使用了,在dllTest.cpp中#include "..\dll1\dll1.h"就可以像下面这样使用了

 

/*
 dll1.cpp
*/
void  Point::output( int  x, int  y)
{
    HWND hwnd
= GetForegroundWindow(); // 获得调用者的窗口的句柄,当前正在使用的窗口
    HDC hdc = GetDC(hwnd);
    
char  buf[ 20 ];
    memset(buf,
0 , 20 );
    sprintf(buf,
" x=%d,y=%d " ,x,y); // 字符数组的格式化
    TextOut(hdc, 0 , 0 ,buf,strlen(buf));
    ReleaseDC(hwnd,hdc);
}

void  Point::test()
{
}

 

/*

dllTest.cpp

*/

void CDllTestDlg::OnBtnOutput() 
{
    
// TODO: Add your control notification handler code here
    Point pt;
    pt.output(
5,3);
}

如果我们不想导出整个类而只是想导出类中的某个成员函数,我们可以这么做


class   /* DLL1_API */  Point
{
public :
    DLL1_API 
void  output( int  x, int  y);//只导出成员函数output
    
void  test();
};

你可能感兴趣的:(dll)