- 浏览: 2046194 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (795)
- java (263)
- 聚类搜索引擎 (9)
- 经验之谈 (67)
- DSP (3)
- C++ (140)
- Linux (37)
- SNMP (6)
- Python (6)
- 数据库 (61)
- 网络 (20)
- 算法 (15)
- 设计模式 (4)
- 笔试题 (38)
- 散文 (35)
- 数据结构 (9)
- 银行知识 (0)
- 榜样 (9)
- Lucene (15)
- Heritrix (6)
- MetaSeeker (0)
- netbeans (12)
- php (3)
- 英语 (8)
- DB2 (0)
- java基础 (5)
- mongodb & hadoop (4)
- Javascript (7)
- Spring (4)
- ibatis & myibatis (1)
- velocity (1)
- 微服务 (0)
- paddle (1)
- 第三方 (0)
- 知识沉淀 (1)
- 建模 (0)
最新评论
-
0372:
标示对java很陌生!
中文乱码解决的4种方式 -
梦留心痕:
Java中\是转意字符, 可是你的这句话我没看懂,只要把得到的 ...
java中如何忽略字符串中的转义字符--转载 -
yanjianpengit:
[b][/b]
java为什么非静态内部类里面不能有静态成员 -
springdata-jpa:
可以参考最新的文档:如何在eclipse jee中检出项目并转 ...
eclipse 如何把java项目转成web项目 -
qq1130127172:
,非常好。
(转)SpringMVC 基于注解的Controller @RequestMapping @RequestParam..
许多单讲C++的书其实都过于学院派,对于真实的工作环境,上百个源文件怎么结合起来,几乎没有提及。
一个最简单的C++程序,只需要一个源文件,这个源文件包含了如下语句
int main(){return 0;}
自然,这个程序什么也不做。
当需程序需要做事情时,我们会把越来越多的语句添加到源文件中,例如,我们会开始在main函数中添加代码:
#include <stdio.h>
int main()
{
printf("Hello World!\n");
return 0;
}
由于人的智力水平的限制,当一个函数中包含了太多的语句时,便不太容易被理解,这时候开始需要子函数:
#include <stdio.h>
void ShowHello()
{
printf("Hello World!\n");
}
int main()
{
ShowHello();
return 0;
}
同样的道理,一个源文件中包含了太多的函数,同样不好理解,人们开始分多个源文件了
// main.cpp
void ShowHello();//[1]
int main()
{
ShowHello();
return 0;
}
// hello.cpp
#include <stdio.h>
void ShowHello()
{
printf("Hello World!\n");
}
将这两个文件加入到一个VC工程中,它们会被分别编译,最后链接在一起。在VC编译器的输出窗口,你可以看到如下信息
--------------------Configuration: hello - Win32 Debug--------------------
Compiling...
main.cpp
hello.cpp
Linking...
hello.exe - 0 error(s), 0 warning(s)
这展示了它们的编译链接过程。
接下来,大家就算不知道也该猜到,当一个工程中有太多的源文件时,它也不好理解,于是,人们想到了一种手段:将一部分源文件预先编译成库文件,也即lib文件,当要使用其中的函数时,只需要链接lib文件就可以了,而不用再理会最初的源文件。
在VC中新建一个static library类型的工程,加入hello.cpp文件,然后编译,就生成了lib文件,假设文件名为hello.lib。
别的工程要使用这个lib有两种方式:
1 在工程选项-〉link-〉Object/Library Module中加入hello.lib
2 可以在源代码中加入一行指令
#pragma comment(lib, "hello.lib")
注意这个不是C++语言的一部分,而是编译器的预处理指令,用于通知编译器需要链接hello.lib
根据个人爱好任意使用一种方式既可。
这种lib文件的格式可以简单的介绍一下,它实际上是任意个obj文件的集合。obj文件则是cpp文件编译生成的,在本例中,lib文件只包含了一个obj文件,如果有多个cpp文件则会编译生成多个obj文件,从而生成的lib文件中也包含了多个obj,注意,这里仅仅是集合而已,不涉及到link,所以,在编译这种静态库工程时,你根本不会遇到链接错误。即使有错,错误也只会在使用这个lib的EXE或者DLL工程中暴露出来。
关于静态lib,就只有这么多内容了,真的很简单,现在我们介绍另外一种类型的lib,它不是obj文件的集合,即里面不含有实际的实现,它只是提供动态链接到DLL所需要的信息。这种lib可以在编译一个DLL工程时由编译器生成。涉及到DLL,问题开始复杂起来,我不指望在本文中能把DLL的原理说清楚,这不是本文的目标,我介绍操作层面的东西。
简单的说,一个DLL工程和一个EXE工程的差别有两点:
1 EXE的入口函数是main或者WinMain,而DLL的入口函数是DllMain
2 EXE的入口函数标志着一段处理流程的开始,函数退出后,流程处理就结束了,而DLL的入口函数对系统来说,只是路过,加载DLL的时候路过一次,卸载DLL的时候又路过一次[2],你可以在DLL入口函数中做流程处理,但这通常不是DLL的目的,DLL的目的是要导出函数供其它DLL或EXE使用。你可以把DLL和EXE的关系理解成前面的main.cpp和hello.cpp的关系,有类似,实现手段不同罢了。
先看如何写一个DLL以及如何导出函数,读者应该先尝试用VC创建一个新的动态链接库工程,创建时选项不选空工程就可以了,这样你能得到一个示例,以便开始在这个例子基础上工作。
看看你创建的例子中的头文件有类似这样的语句:
#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
这就是函数的导出与使用导出函数的全部奥妙了。你的DLL工程已经在工程设置中定义了一个宏DLL_EXPORTS,因此你的函数声明只要前面加DLL_API就表示把它导出,而DLL的使用者由于没有定义这个宏,所以它包含这个头文件时把你的函数看作导入的。通过模仿这个例子,你就可以写一系列的标记为导出的函数了。
导出函数还有另一种方法,是使用DEF文件,DEF文件的作用,在现在来说只是起到限定导出函数名字的作用,这里,我们要引出第二种[4]使用DLL的方法:称为显示加载,通过Windows API的LoadLibrary和GetProcAddress这两个函数来实现[5],这里GetProcAddress的参数需要一个字符串形式的函数名称,如果DLL工程中没有使用DEF文件,那么很可能你要使用非常奇怪的函数名称(形如:?fnDll@@YAHXZ)才能正确调用,这是因为C++中的函数重载机制把函数名字重新编码了,如果使用DEF文件,你可以显式指定没编码前的函数名。
有了这些知识,你可以开始写一些简单的DLL的应用,但是我可以百分之百的肯定,你会遇到崩溃,而之前的非DLL的版本则没有问题。假如你通过显式加载来使用DLL,有可能会是调用约定不一致而引起崩溃,所谓调用约定就是函数声明前面加上__stdcall __cdecl等等限定词,注意一些宏如WINAPI会定义成这些限定词之一,不理解他们没关系,但是记住一定要保持一致,即声明和定义时一致,这在用隐式加载时不成问题,但是显示加载由于没有利用头文件,就有可能产生不一致。
调用约定并不是我真正要说的,虽然它是一种可能。我要说的是内存分配与释放的问题。请看下面代码:
void foo(string& str)
{
str = "hello";
}
int main()
{
string str;
foo(str);
printf("%s\n", str.c_str());
return 0;
}
当函数foo和main在同一个工程中,或者foo在静态库中时,不会有问题,但是如果foo是一个DLL的导出函数时,请不要这么写,它有可能会导致崩溃[6]。崩溃的原因在于“一个模块中分配的内存在另一个模块中释放”,DLL与EXE分属两个模块,例子中foo里面赋值操作导致了内存分配,而main中return语句之后,string对象析构引起内存释放。
我不想穷举全部的这类情况,只请大家在设计DLL接口时考虑清楚内存的分配释放问题,请遵循谁分配,谁释放的原则来进行。
如果不知道该怎么设计,请抄袭我们常见的DLL接口--微软的API的做法,如:
CreateDC
ReleaseDC
的成对调用,一个函数分配了内存,另外一个函数用来释放内存。
回到我们有可能崩溃的例子中来,怎么修改才能避免呢?
这可以做为一个练习让读者来做,这个练习用的时间也许会比较长,如果你做好了,那么你差不多就出师了。一时想不到也不用急,我至少见过两个有五年以上经验的程序员依然犯这样的错误。
注[1]:为了说明的需要,我这里使用直接声明的方式,实际工程中是应该使用头文件的。
注[2]: 还有线程创建与销毁也会路过DLL的入口,但是这对新手来说意义不大。
注[3]:DEF文件格式很简单,关于DEF文件的例子,可以通过新建一个ATL COM工程看到。
注[4]:第一种方法和使用静态库差不多,包含头文件,链接库文件,然后就像是使用普通函数一样,称为隐式加载。
注[5]:具体调用方法请参阅MSDN。
注[6]:之所以说有可能是因为,如果两个工程的设置都是采用动态连接到运行库,那么分配释放其实都在运行库的DLL中进行,那么这种情况便不会发生崩溃
一个最简单的C++程序,只需要一个源文件,这个源文件包含了如下语句
int main(){return 0;}
自然,这个程序什么也不做。
当需程序需要做事情时,我们会把越来越多的语句添加到源文件中,例如,我们会开始在main函数中添加代码:
#include <stdio.h>
int main()
{
printf("Hello World!\n");
return 0;
}
由于人的智力水平的限制,当一个函数中包含了太多的语句时,便不太容易被理解,这时候开始需要子函数:
#include <stdio.h>
void ShowHello()
{
printf("Hello World!\n");
}
int main()
{
ShowHello();
return 0;
}
同样的道理,一个源文件中包含了太多的函数,同样不好理解,人们开始分多个源文件了
// main.cpp
void ShowHello();//[1]
int main()
{
ShowHello();
return 0;
}
// hello.cpp
#include <stdio.h>
void ShowHello()
{
printf("Hello World!\n");
}
将这两个文件加入到一个VC工程中,它们会被分别编译,最后链接在一起。在VC编译器的输出窗口,你可以看到如下信息
--------------------Configuration: hello - Win32 Debug--------------------
Compiling...
main.cpp
hello.cpp
Linking...
hello.exe - 0 error(s), 0 warning(s)
这展示了它们的编译链接过程。
接下来,大家就算不知道也该猜到,当一个工程中有太多的源文件时,它也不好理解,于是,人们想到了一种手段:将一部分源文件预先编译成库文件,也即lib文件,当要使用其中的函数时,只需要链接lib文件就可以了,而不用再理会最初的源文件。
在VC中新建一个static library类型的工程,加入hello.cpp文件,然后编译,就生成了lib文件,假设文件名为hello.lib。
别的工程要使用这个lib有两种方式:
1 在工程选项-〉link-〉Object/Library Module中加入hello.lib
2 可以在源代码中加入一行指令
#pragma comment(lib, "hello.lib")
注意这个不是C++语言的一部分,而是编译器的预处理指令,用于通知编译器需要链接hello.lib
根据个人爱好任意使用一种方式既可。
这种lib文件的格式可以简单的介绍一下,它实际上是任意个obj文件的集合。obj文件则是cpp文件编译生成的,在本例中,lib文件只包含了一个obj文件,如果有多个cpp文件则会编译生成多个obj文件,从而生成的lib文件中也包含了多个obj,注意,这里仅仅是集合而已,不涉及到link,所以,在编译这种静态库工程时,你根本不会遇到链接错误。即使有错,错误也只会在使用这个lib的EXE或者DLL工程中暴露出来。
关于静态lib,就只有这么多内容了,真的很简单,现在我们介绍另外一种类型的lib,它不是obj文件的集合,即里面不含有实际的实现,它只是提供动态链接到DLL所需要的信息。这种lib可以在编译一个DLL工程时由编译器生成。涉及到DLL,问题开始复杂起来,我不指望在本文中能把DLL的原理说清楚,这不是本文的目标,我介绍操作层面的东西。
简单的说,一个DLL工程和一个EXE工程的差别有两点:
1 EXE的入口函数是main或者WinMain,而DLL的入口函数是DllMain
2 EXE的入口函数标志着一段处理流程的开始,函数退出后,流程处理就结束了,而DLL的入口函数对系统来说,只是路过,加载DLL的时候路过一次,卸载DLL的时候又路过一次[2],你可以在DLL入口函数中做流程处理,但这通常不是DLL的目的,DLL的目的是要导出函数供其它DLL或EXE使用。你可以把DLL和EXE的关系理解成前面的main.cpp和hello.cpp的关系,有类似,实现手段不同罢了。
先看如何写一个DLL以及如何导出函数,读者应该先尝试用VC创建一个新的动态链接库工程,创建时选项不选空工程就可以了,这样你能得到一个示例,以便开始在这个例子基础上工作。
看看你创建的例子中的头文件有类似这样的语句:
#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
这就是函数的导出与使用导出函数的全部奥妙了。你的DLL工程已经在工程设置中定义了一个宏DLL_EXPORTS,因此你的函数声明只要前面加DLL_API就表示把它导出,而DLL的使用者由于没有定义这个宏,所以它包含这个头文件时把你的函数看作导入的。通过模仿这个例子,你就可以写一系列的标记为导出的函数了。
导出函数还有另一种方法,是使用DEF文件,DEF文件的作用,在现在来说只是起到限定导出函数名字的作用,这里,我们要引出第二种[4]使用DLL的方法:称为显示加载,通过Windows API的LoadLibrary和GetProcAddress这两个函数来实现[5],这里GetProcAddress的参数需要一个字符串形式的函数名称,如果DLL工程中没有使用DEF文件,那么很可能你要使用非常奇怪的函数名称(形如:?fnDll@@YAHXZ)才能正确调用,这是因为C++中的函数重载机制把函数名字重新编码了,如果使用DEF文件,你可以显式指定没编码前的函数名。
有了这些知识,你可以开始写一些简单的DLL的应用,但是我可以百分之百的肯定,你会遇到崩溃,而之前的非DLL的版本则没有问题。假如你通过显式加载来使用DLL,有可能会是调用约定不一致而引起崩溃,所谓调用约定就是函数声明前面加上__stdcall __cdecl等等限定词,注意一些宏如WINAPI会定义成这些限定词之一,不理解他们没关系,但是记住一定要保持一致,即声明和定义时一致,这在用隐式加载时不成问题,但是显示加载由于没有利用头文件,就有可能产生不一致。
调用约定并不是我真正要说的,虽然它是一种可能。我要说的是内存分配与释放的问题。请看下面代码:
void foo(string& str)
{
str = "hello";
}
int main()
{
string str;
foo(str);
printf("%s\n", str.c_str());
return 0;
}
当函数foo和main在同一个工程中,或者foo在静态库中时,不会有问题,但是如果foo是一个DLL的导出函数时,请不要这么写,它有可能会导致崩溃[6]。崩溃的原因在于“一个模块中分配的内存在另一个模块中释放”,DLL与EXE分属两个模块,例子中foo里面赋值操作导致了内存分配,而main中return语句之后,string对象析构引起内存释放。
我不想穷举全部的这类情况,只请大家在设计DLL接口时考虑清楚内存的分配释放问题,请遵循谁分配,谁释放的原则来进行。
如果不知道该怎么设计,请抄袭我们常见的DLL接口--微软的API的做法,如:
CreateDC
ReleaseDC
的成对调用,一个函数分配了内存,另外一个函数用来释放内存。
回到我们有可能崩溃的例子中来,怎么修改才能避免呢?
这可以做为一个练习让读者来做,这个练习用的时间也许会比较长,如果你做好了,那么你差不多就出师了。一时想不到也不用急,我至少见过两个有五年以上经验的程序员依然犯这样的错误。
注[1]:为了说明的需要,我这里使用直接声明的方式,实际工程中是应该使用头文件的。
注[2]: 还有线程创建与销毁也会路过DLL的入口,但是这对新手来说意义不大。
注[3]:DEF文件格式很简单,关于DEF文件的例子,可以通过新建一个ATL COM工程看到。
注[4]:第一种方法和使用静态库差不多,包含头文件,链接库文件,然后就像是使用普通函数一样,称为隐式加载。
注[5]:具体调用方法请参阅MSDN。
注[6]:之所以说有可能是因为,如果两个工程的设置都是采用动态连接到运行库,那么分配释放其实都在运行库的DLL中进行,那么这种情况便不会发生崩溃
发表评论
-
DLL中导出函数的声明有两种方式:
2012-11-12 16:42 1863DLL中导出函数的声明有两种方式: 一种方式是:在函数声明中 ... -
k-means算法的C++实现
2011-04-05 11:38 2343k-means算法的C++实现: http://www.ku ... -
main()中的参数
2010-10-31 10:41 1540所有的应用程序都是从以main函数作为入口, 而mai ... -
static作用
2010-10-26 19:15 2390转自(from http://www.cnb ... -
mmap函数
2010-10-25 22:41 1915mmap函数的使用方法 UNIX ... -
C语言中三种内存分配方式
2010-10-25 20:23 01.malloc 原型:extern void *ma ... -
位拷贝和值拷贝
2010-10-23 15:37 1601为了便于说明我们以String类为例: 首先定义String ... -
(转帖)把类的析构函数写成虚函数的用意
2010-10-23 15:10 1702#include <iostream.h> cl ... -
动态规划/贪心算法----0/1背包问题AND普通背包问题
2010-10-23 14:03 6829两个背包问题都是比较典型的问题,对这两种算法的理解有很好的帮助 ... -
netstat, nslookup, finger, ping命令
2010-10-22 17:13 1535Netstat用于显示与IP、TCP ... -
C++返回值
2010-10-22 16:53 1551C++函数返回值: (1)正常情况下,函数的参数要复制一份在 ... -
switch语句后的表达式的值
2010-10-22 16:23 1844一般格式: switch (表达式) { case 常量 ... -
C++四种强制类型转换
2010-10-19 11:45 1579显式类型转换又被称之 ... -
C++四种强制类型转化的区别
2010-10-19 11:43 1359先介绍const_cast和reinterpret_cast: ... -
Visual C++线程同步技术剖析:临界区,时间,信号量,互斥量
2010-10-18 14:24 1835使线程同步 在程序中使用多线程时,一般很少有多个线程能在其 ... -
(转)临界区,互斥量,信号量,事件的区别
2010-10-18 14:22 1774四种进程或线程同步互斥的控制方法1、临界区:通过对多线程的串行 ... -
(转)在C++中实现同步锁,类似synchronize(object){....}
2010-10-18 13:49 1886在做C++的项目中发现, ... -
C++线程同步
2010-10-18 13:46 1619线程同步是多 ... -
C++多线程编程
2010-10-18 10:56 1755今天我给大家讲一讲C++ ... -
关于C++对函数传参与函数返回值进行引用传递的详解
2010-10-16 22:51 4063关于C++对函数传参与函数返回值进行引用传递的详解 ...
相关推荐
本教程将详细介绍如何制作DLL接口以及如何在应用程序中使用这些接口。 首先,我们需要理解DLL接口的基本概念。接口是一组函数或方法的集合,它定义了DLL对外提供的服务。在C++中,通常通过纯虚函数的类来实现接口。...
【VC++制作DLL教程】 创建DLL(动态链接库)在软件开发中是非常常见的技术,它允许将一组功能封装在一个库中,供多个应用程序共享。在VC++环境下,我们可以选择两种方式来创建DLL:基于API的传统DLL和基于MFC的对象...
下面我们将深入探讨如何在VB中制作DLL以及如何在VB项目中引用DLL。 一、制作VB DLL 1. **创建新项目**:打开Visual Basic,选择“工程”菜单,然后点击“添加新项目”。在弹出的对话框中,选择“类库”模板,这将...
制作DLL图标的过程涉及到图像设计和编程技术,下面我们将详细探讨如何制作DLL图标以及相关的知识点。 首先,我们需要了解图标的基本结构。图标文件(.ICO)是由多个尺寸和颜色深度的图像组成的,包括16x16、32x32、...
本文主要讨论如何使用C#制作DLL(动态链接库)文件,这是一种能够提高代码重用性和应用程序可维护性的技术。DLL文件通常包含一组可由多个程序共享的函数或类。 在C#中,创建DLL文件的过程与传统编程语言如Visual ...
总结一下,本教程“教你用VB制作DLL”涵盖了从零开始创建DLL,到在其他VB项目中使用和注册DLL的完整流程。通过学习和实践,你不仅能掌握VB DLL的基本操作,还能深入理解Windows编程中的动态链接机制,这对于任何VB...
DLL(Dynamic Link Library)是Windows操作系统中的...制作DLL时要注意导出函数的声明和实现,以及在使用DLL时的加载、调用和卸载步骤。在实际开发中,理解并掌握DLL的使用对于提升软件开发效率和质量有着显著的作用。
RadASM制作DLL文件并注入一个位图启动 有几个方法将位图启动注入一个二进制文件。不幸的是非常不易。本教程中显示的方式是我遇到的最简单的,虽然可能有更简单的选择。问题是位图是一种资源,为此在一个现有的二进制...
dll内放置改写,读取注册表的function function regkeyexists(x:shortstring):boolean;stdcall;External 'reg.dll' function regkeydelete(x:shortstring):boolean;stdcall;External 'reg.dll' function ...
**BCB制作DLL文件入门详解** DLL(Dynamic Link Library)是Windows操作系统中的一种共享库,它包含可由多个程序同时使用的代码和数据。在C++编程中,使用Borland C++ Builder(简称BCB)制作DLL文件可以提高代码...
### Visual Studio 2005 制作DLL文件详解 #### 一、创建DLL项目 在Visual Studio 2005中制作DLL文件的过程相对较为简单。首先,我们需要打开Visual Studio 2005,然后按照以下步骤进行操作: 1. **启动Visual ...
2. **C++制作DLL**: - DLL是一种可重用的代码和数据容器,它包含可由多个程序同时使用的函数和其他资源。在C++中,创建DLL需要定义导出函数,使用`__declspec(dllexport)`关键字标记,编译为动态库文件。 3. **...
DLL图标文件制作工具IconJack32---提取EXE图标制作DLL工具
将一些方法封装到DLL中给人用,第一可以保存自己的源码不被别人看到,另外也为主程序解脱了一大部分
在Delphi中制作DLL(动态链接库)文件是一项常见的任务,用于创建可重用的代码库,供其他应用程序调用。DLL不仅可以减小程序体积,还可以提高性能,因为多个程序可以共享同一块内存中的代码。 DLL文件的制作过程...
总结,制作DLL文件涉及编写导出函数的源代码,创建DLL项目,构建DLL文件,以及在其他应用程序中加载和使用这些函数。通过这种方式,我们可以实现代码复用,优化系统资源,并简化软件开发过程。在实际应用中,DLL文件...
### Visual Studio 2005 制作DLL文件详解 #### 概述 在软件开发过程中,动态链接库(Dynamic Link Library,简称DLL)是非常重要的组成部分。它允许开发者将经常被多个程序使用的功能封装到单独的文件中,从而提高...
### Visual Studio 2005 制作DLL文件详解 #### 概述 本文将详细介绍如何使用Visual Studio 2005创建不同类型的DLL(动态链接库)文件,并重点讲解与MFC(Microsoft Foundation Classes)类库相关的DLL开发。根据...
这篇“vs2008制作dll笔记,回带值样例”是关于如何在VS2008中创建DLL以及如何在其他项目中调用这些DLL的教程。下面我们将深入探讨这个话题。 首先,DLL是一种包含可由多个程序同时使用的函数和资源的库。这样可以...
接口模式的制作DLL方法向导 释放的目标文件夹就是 DLL工程文件夹 释放完成后,输入DLL的文件名 优点: 1、DLL只导出固定的两个方法,获取接口和释放接口函数。 象普通类一样使用接口的方法; 2、释放非常干净,可...