现在的位置: 首页 > 综合 > 正文

关于vc下dll的相关知识

2014年09月05日 ⁄ 综合 ⁄ 共 4804字 ⁄ 字号 评论关闭

1.  Vc下生成,如果只是cpp使用,只需要在函数前加 _declspec(dllexport)就可导出,可以通过vc工具dumpbin exports查看。

2.  Vc6下可以建立一个空的dll,里面什么文件都没有,这时只要新建一个cpp文件,里面放一个函数,然后函数头上加入标示:_declspec(dllexport)就可以生产带有函数输出的dll了。

3.  外部使用需求:

l  extern 引用函数(extern在这里可以被-declspec(dllimport) 替代),extern可以用在多个场和,但-declspec(dllimport)只能用在dll中,但是效率更高。

l  dll文件copydebug目录下,其实只要是vc的寻找目录下都可以

l  lib文件copy到项目目录下(不要搞错了,否则找不到)

l  在工程-》设置-link的对象/库模块下 添加.lib文件名称。

就是这么简单。

备注:输入库lib并没有提供代码,而是用来为链接程序提供信息,以便建立重定位表。也可以通过dumpbin –imports 来查看exe的输入信息。

4.  可以通过vc6下的工具dependency walker来查看可执行模块或者动态链接库的依赖。此工具如何查看exe的依赖dll,如果是自己写的dll,最好放到debug目录下,否则dependency会显示找不到这个dll

5.  为了方便别人查看自己的dll,一般dll编写者都会提供一个头文件,来包含自己导出函数的声明。别人只需要引用这个头文件就可。而且头文件里最基础的只需要添加这个函数的声明就可以,但是要加标头,如:

-declspec(dllimport) int add(int a, int b); 这时,如果使用端copy.h文件过去,然后文中的add的声明就可以去掉。

6.  如何让一个头文件既可以为客户端服务,又同时为自己dll服务(此时dll编写者的cpp文件中也可以把声明去掉),需要条件编译:

那么就需要使用条件编译。如:

#ifdef qiaoDLL_API

#else

#define qiaoDLL_API _declspec(dllimport)

#endif

原来的cpp文件中:_declspec(dllexport)需要define宏:(这个#define要放在#include之前)

#define qiaoDLL_API _declspec(dllexport)

分析:由于vc中:头文件不参与编译,源文件单独编译。

因此:编译cpp时,由与已经定义了qiaoDLL_API,所以包含头文件时,就不会再定义,此时的qiaoDLL_API指的就是_declspec(dllexport)。而如果客户使用,那么只要他们不定义qiaoDLL_API,因为他们引入了.h文件,那么根据条件编译的结果,他们使用的就是qiaoDLL_API 就指代_declspec(dllimport),从而达到引入目的。

7.  Vc6中是可以通过dll导出cpp的类的。导出类要求:同样要在.h文件中声明这个类,如:

Class qiaoDLL_API myClass

{

   Void Output(int x, int y);

}

qiaoDLL_API标识符加在类前面就表示把整个类导出,如果只想导出类中的一部分函数,那么就需要去掉这个标识,然后把标识加在里面的函数头前。

8.  在类中实现一个方法:这里方法把结果显示在调用者的窗口上。如何实现:

使用windowsAPI函数来实现:

void 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);

还要包含两个头文件:

1.       API对应 <windows.h>

2.       标准输入输出文件 <stdio.h>

客户端使用很简单:

MyClass mc;

mc.Output(10,20); 就可以了。

 

9.  由于 CPP考虑到重载,故将函数名在dll输出时发生变化,这时如果用c语言编写的客户端访问,可能就会出错。怎么办呢?

这时就需要在声明前加入 extern “C” 其中C必须大写。

#define qiaoDLL_API extern “C ” _declspec(dllimport)

#define qiaoDLL_API extern “C ” _declspec(dllexport)

此时用dumpbin查看就会发现,导出的函数名字和原来的名字没有发生改变。

缺陷:如果使用了extern “C” 就不能使用类的成员函数,只能导出全局函数,让全局函数不发生名字改变。另外,如果导出的函数调用约定发生改变,即使用了extern “C” ,名字也会发生改变。如:

在函数前加入 _stdcall (也就是winapi、也就是pascal),(不加这个标识,默认是c调用方式)。此时,即使你使用了extern “C”, 通过dumpbin查看发现了 函数名字也发生了改变。

小知识:dephi采用的就是这种_stdcall(pascal)调用方式。

但是名字发生了改变,该如何兼容cdephi呢?

模块定义文件,工程名.def。加入工程后。编辑:

LIBRARY 工程名

EXPORTS

add  (也可以用newname=add来指定新的名字)

此处的add就表示如果搜索cpp中有和add匹配的,那么就用此处的add作为输出函数名,不管是什么调用方式。【有意思的,如果使用新的名字,那么在vc++使用此dll时,两个名字都可以作为函数名使用,不过要修改.h文件】

 

10 前面的方式是隐式链接,当链接的dll比较多时,建议采用动态链接,示例:

HINSTANCE hInst;

hInst=LoadLibrary(“dll.dll”);

typedef int(*Addproc)(int a,int b);

Addproc add=(Addproc)GetProcAddress(hInst,”add”);

If(!add)

{

MessageBox(“获取函数失败”);

Return;

}

CString str;

Str.Format(“5+3=%d”,Add(5,3));

MessageBox(str);

需要注意的是,如果程序引用的dll比较少,而且多次用到dll中函数,建议采用隐式,但是如果dll比较多,每个引用的函数又比较少,那么建议采用显示隐藏。因为隐式加载过多的dll,程序启动时加载资源过多,启动缓慢。

需要注意的是,如果前面的约定为_stdcall,那么在动态调用时也必须加上这个约定。即:

typedef int(_stdcall *Addproc)(int a,int b);    //加上这个约定

否则就会报错。

10.              动态加载时,用dumpbin –imports 查看exe就会发现没有这个dll的信息。

11.              动态加载时,如果原来的没有采用extern “C” 或者def,此时动态链接库就会找不到函数,因为函数名称已经发生了变化(隐式加载时时可以找到的)。Oh,god,原来我的问题是出在这里啊。

12.              动态加载时,也可以通过序号访问函数,但是由于要将数值转化为LPCTSTR,可以用一个宏 MAKEINTRESOURCE(1);

13.              关于DllMain函数,可有可无,可以传递当前dll的句柄。第二个参数是关于加载原因,可以case使用。形式:

 

这里附上一个vc6自动生成的含有函数、类、全局变量的带DllMainDLL

// qiaoDll.cpp : Defines the entry point for the DLL application.

//

 

#include "stdafx.h"

#include "qiaoDll.h"

 

BOOL APIENTRY DllMain( HANDLE hModule,

                       DWORD  ul_reason_for_call,

                       LPVOID lpReserved

                                                )

{

    switch (ul_reason_for_call)

         {

                   case DLL_PROCESS_ATTACH:

                   case DLL_THREAD_ATTACH:

                   case DLL_THREAD_DETACH:

                   case DLL_PROCESS_DETACH:

                            break;

    }

    return TRUE;

}

 

 

// This is an example of an exported variable

QIAODLL_API int nQiaoDll=0;

 

// This is an example of an exported function.

QIAODLL_API int fnQiaoDll(void)

{

         return 42;

}

 

// This is the constructor of a class that has been exported.

// see qiaoDll.h for the class definition

CQiaoDll::CQiaoDll()

{

         return;

}

 

 

// The following ifdef block is the standard way of creating macros which make exporting

// from a DLL simpler. All files within this DLL are compiled with the QIAODLL_EXPORTS

// symbol defined on the command line. this symbol should not be defined on any project

// that uses this DLL. This way any other project whose source files include this file see

// QIAODLL_API functions as being imported from a DLL, wheras this DLL sees symbols

// defined with this macro as being exported.

#ifdef QIAODLL_EXPORTS

#define QIAODLL_API __declspec(dllexport)

#else

#define QIAODLL_API __declspec(dllimport)

#endif

 

// This class is exported from the qiaoDll.dll

class QIAODLL_API CQiaoDll {

public:

         CQiaoDll(void);

         // TODO: add your methods here.

};

 

extern QIAODLL_API int nQiaoDll;

 

QIAODLL_API int fnQiaoDll(void);

14.              有的时候,报没有编译头的错,是因为需要StdAfx.h,可以填上或者到设置中把link中把编译头设置为自动或者不需要。

15.  也可以直接用mfc设计dll

参考了孙鑫老师的讲课,特感谢。

抱歉!评论已关闭.