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();
};

posted on 2009-01-13 12:40  风荷小筑  阅读(860)  评论(0编辑  收藏  举报