编写DLL
想想还是把这个记录下吧,虽然不难,但由于平时写得不多,老是搞忘了。
1、我们来编写一个简单的DLL程序。
首先,我们来看下入口函数DllMain()。DllMain()有3个参数:
(1)hModule:DLL模块的句柄。
(2)ul_reason_for_call:DllMain函数被调用的原因。其取值有4种,分别是DLL_PROCESS_ATTACH(当DLL被某进程加载时DllMain被调用)、DLL_PROCESS_DETACH(当DLL被某进程卸载时DllMain被调用)、DLL_THREAD_ATTACH(进程中有线程被创建时DllMain被调用)、DLL_THREAD_DETACH(进程中有线程结束时DllMain被调用)。
(3)lpReserved:保留项。
函数前面的APIENTRY是一个宏,定义如下:
#define APIENTRY WINAPI
WINAPI也是一个宏,表示一种函数调用约定。
我们需要对DllMain()进行一下填充,加个switch。后面详见例子。我们还需要为之添加一个简单的导出函数。该函数定义如下:
extern "C" __declspec(dllexport) VOID MsgBox(char *szMsg);
extern "C"表示该函数以C方式导出。
其实现如下:
VOID MsgBox(char *szMsg){
char szModuleName[MAX_PATH]={};
GetModuleFileName(NULL,szModuleName,MAX_PATH);
MessageBox(NULL,szMsg,szModuleName,MB_OK);
}
运行函数后弹出一个对话框,显示一个字符串,并显示其所在的进程的进程名。我们分别在DLL_PROCESS_ATTACH和DLL_PROCESS_DETACH下加一个对该函数的调用。
如下:
#include <windows.h>
extern "C" __declspec(dllexport) VOID MsgBox(char *szMsg);
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){
switch(ul_reason_for_call){
case DLL_PROCESS_ATTACH:
MsgBox("DLL_PROCESS_ATTACH");
break;
case DLL_PROCESS_DETACH:
MsgBox("DLL_PROCESS_DETACH");
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
VOID MsgBox(char *szMsg){
char szModuleName[MAX_PATH]={};
GetModuleFileName(NULL,szModuleName,MAX_PATH);
MessageBox(NULL,szMsg,szModuleName,MB_OK);
}
编译该代码,会生成两个好玩的文件:6_10_4.dll和6_10_4.lib。前面是DLL文件,后面是库文件。
2、静态调用:
创建一个控制台程序,建立一个cpp文件,添加代码如下:
#include <windows.h> extern "C" VOID MsgBox(char *szMsg);
#pragma comment(lib,"6_10_4") int main(int argc,char* argv[]){
MsgBox("hello first dll!");
return ;
}
对该代码直接编译链接,会报错:无法找到6_10_4.lib文件。把这个文件复制到这个cpp目录下,运行会报错,提示没有找到6_10_4.dll文件。同样,也需要把该文件置于cpp目录下。至此,没有问题了,运行依次弹出三个对话框:



3、动态调用:
静态调用就是在编译程序时便把dll信息写入程序里,而动态调用则是在运行时导入dll信息。
控制台程序cpp代码如下:
#include <windows.h>
typedef VOID (*PFUNMSG)(char *);
int main(int argc,char* argv[]){
HMODULE hModule=LoadLibrary("6_10_4.dll");
if(hModule==NULL){
MessageBox(NULL,"6_10_4.dll文件不存在","DLL加载失败",MB_OK);
return -;
}
PFUNMSG pFunMsg=(PFUNMSG)GetProcAddress(hModule,"MsgBox");
pFunMsg("hello first dll!");
return ;
}
这里不要求dll文件一定在cpp目录下,在LoadLibrary函数里写入dll的路径即可。运行结果与前面的一样。
4、其他
对于未文档化的API,或是没有提供头文件的API,我们可以利用LoadLibrary()和GetProcAddress()这两个API函数来实现对前面那些API的调用。
LoadLibrary():
HMODULE LoadLibrary(
LPCTSTR lpFileName //file name of module
);
该函数只有一个参数,即要加载的DLL文件的路径。
GetProcAddress():
GetProcAddress(
HMODULE hModule, //handle to DLL module
LPCSTR lpProcName //function name
);
该函数有两个参数,hModule是模块的句柄,lpProcName指定要获取函数地址的函数名称。
另外说下,vc6自带的工具"Depends"挺好用的,可以查看dll程序的导出函数等。它在:菜单“开始”->“程序”->“Microsoft Visual Studio 6.0”->“Microsoft Visual Studio 6.0 Tools”->“Depends”。
编写DLL的更多相关文章
- QT编写DLL给外部程序调用,提供VC/C#/C调用示例(含事件)
最近这阵子,接了个私活,封装一个开发包俗称的SDK给客户调用,查阅了很多人家的SDK,绝大部分用VC编写,而且VC6.0居多,估计也是为了兼容大量的XP用户及IE浏览器,XP自带了VC6.0运行库,所 ...
- delphi编写dll心得, 谢谢原作者的分享。转
delphi编写dll心得 1.每个函数体(包括exports和非exports函数)后面加 'stdcall;', 以编写出通用的dll2.exports函数后面必须加'export;'(放在'st ...
- 如何编写Dll(用命令行编译加深理解)
DLL的优点 简单的说,dll有以下几个优点: 1) 节省内存.同一个软件模块,若是以源代码的形式重用,则会被编译到不同的可执行程序中,同时运行这些exe时这些模块的二进制码会被重复加载到内 ...
- VC2010编写Dll文件(转)
源:VC2010编写Dll文件 1. 打开VS2010[Flie / New / Project / Visual C++ / Win32 / Win32 Console Application]在下 ...
- 编写dll时的内存分配策略
前一篇文章介绍了为何要共用内存管理器,有人要问可不可以在编写dll时更通用一些,可以兼顾其它编译器(如果是其它编译器的话,Delphi写的dll不能与其它语言共用内存管理器),采用一定的策略来避免在d ...
- C++编写DLL动态链接库的步骤与实现方法
原文:http://www.jb51.net/article/90111.htm 本文实例讲述了C++编写DLL动态链接库的步骤与实现方法.分享给大家供大家参考,具体如下: 在写C++程序时,时常需要 ...
- Delphi 编写DLL动态链接库文件的知识
一.DLL动态链接库文件的知识简介: Windows的发展要求允许同时运行的几个程序共享一组函数的单一拷贝.动态链接库就是在这种情况下出现的.动态链接库不用重复编译或链接,一旦装入内存,Dlls函数可 ...
- Delphi调用C# 编写dll动态库
Delphi调用C# 编写dll动态库 编写C#dll的方法都一样,首先在vs2005中创建一个“类库”项目WZPayDll, using System.Runtime.InteropServices ...
- Delphi 编写DLL动态链接库文件的知识和样例(有详细步骤,很清楚)
一.DLL动态链接库文件的知识简介: Windows的发展要求允许同时运行的几个程序共享一组函数的单一拷贝.动态链接库就是在这种情况下出现的.动态链接库不用重复编译或链接,一旦装入内存,Dlls函数可 ...
- windows下编写dll
dll的优点 简单的说,dll有以下几个优点: 1) 节省内存.同一个软件模块,若是以源代码的形式重用,则会被编译到不同的可执行程序中,同时运行这些exe时这些模块的二进制码会被重复加载到内存中.如果 ...
随机推荐
- 基于jquery 的ajax 文件下载
ajax 文件下载,实际上就是模拟表单提交,代码如下: function download(url, data, method){ //url and data options required if ...
- elipse中开发servlet,直接run as 时出现404错误的解决方法
在elipse中开发servlet时,无论在IDE中运行,还是在浏览器中访问servlet时,一直报404错误, 后发现在build目录中没有生成相应的类文件,后反复采用project中的clean. ...
- php mysql apache 的字符集
在使用中常常遇到utf-8和utf8,现在终于弄明白他们的使用不同之处了,现在来和大家分享一下,下面我们看一下utf8 和 UTF-8 有什么区别 “UTF-8”是标准写法,php在Windows下边 ...
- C# WMP 视频播放
1. C#播放器控件常用的方法介绍 右击工具箱->选择项(I)... -> 显示"选择工具箱项" -> COM组件 -> Windows Media Pla ...
- 关于RedHat Enterprise Linux 6.4使用Centos 6 的yum源
思路:卸载redhat自带yum,然后下载centos的yum,安装后修改配置文件 1.首先到http://mirrors.163.com/centos下载软件包 x86 地址:http://mirr ...
- (OPC Client .NET 开发类库)网上很多网友都有提过,.NET开发OPC Client不外乎下面三种方法
1. 背景 OPC Data Access 规范是基于COM/DCOM定义的,因此大多数的OPC DA Server和client都是基于C++开发的,因为C++对COM/DCOM有最好的支持.现在, ...
- Vue.js实现数据的双向数据流
众所周知,Vue.js一直使用的是单向数据流的,和angularJs的双向数据流相比,单向数据流更加容易控制.Vue.js允许父组件通过props属性传递数据到子组件.但是有些情况下我们需要在子组件里 ...
- Django CBV与FBV
FBV FBV(function base views) 就是在视图里使用函数处理请求. CBV CBV(class base views) 就是在视图里使用类处理请求. Python是一个面向对象的 ...
- MySQL5.7.18,初始化完成登录,提示密码过
初始化完成登录,提示密码过期 原因: 安装CentOs的时候,默认安装了mysql,并且版本与自己安装的mysql版本不一致,直接使用mysql -uroot -p'password'连接,默认调用的 ...
- dom4J使用笔记
使用dom4j是目前最常用的解析XML的方法,dom4j解析集DOM和SAX技术优点于一身,要使用dom4j 还是先要导入jar: dom4j-1.6.1.jar (dom4j最主要的jar包,可以独 ...