想想还是把这个记录下吧,虽然不难,但由于平时写得不多,老是搞忘了。

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的更多相关文章

  1. QT编写DLL给外部程序调用,提供VC/C#/C调用示例(含事件)

    最近这阵子,接了个私活,封装一个开发包俗称的SDK给客户调用,查阅了很多人家的SDK,绝大部分用VC编写,而且VC6.0居多,估计也是为了兼容大量的XP用户及IE浏览器,XP自带了VC6.0运行库,所 ...

  2. delphi编写dll心得, 谢谢原作者的分享。转

    delphi编写dll心得 1.每个函数体(包括exports和非exports函数)后面加 'stdcall;', 以编写出通用的dll2.exports函数后面必须加'export;'(放在'st ...

  3. 如何编写Dll(用命令行编译加深理解)

    DLL的优点 简单的说,dll有以下几个优点: 1)      节省内存.同一个软件模块,若是以源代码的形式重用,则会被编译到不同的可执行程序中,同时运行这些exe时这些模块的二进制码会被重复加载到内 ...

  4. VC2010编写Dll文件(转)

    源:VC2010编写Dll文件 1. 打开VS2010[Flie / New / Project / Visual C++ / Win32 / Win32 Console Application]在下 ...

  5. 编写dll时的内存分配策略

    前一篇文章介绍了为何要共用内存管理器,有人要问可不可以在编写dll时更通用一些,可以兼顾其它编译器(如果是其它编译器的话,Delphi写的dll不能与其它语言共用内存管理器),采用一定的策略来避免在d ...

  6. C++编写DLL动态链接库的步骤与实现方法

    原文:http://www.jb51.net/article/90111.htm 本文实例讲述了C++编写DLL动态链接库的步骤与实现方法.分享给大家供大家参考,具体如下: 在写C++程序时,时常需要 ...

  7. Delphi 编写DLL动态链接库文件的知识

    一.DLL动态链接库文件的知识简介: Windows的发展要求允许同时运行的几个程序共享一组函数的单一拷贝.动态链接库就是在这种情况下出现的.动态链接库不用重复编译或链接,一旦装入内存,Dlls函数可 ...

  8. Delphi调用C# 编写dll动态库

    Delphi调用C# 编写dll动态库 编写C#dll的方法都一样,首先在vs2005中创建一个“类库”项目WZPayDll, using System.Runtime.InteropServices ...

  9. Delphi 编写DLL动态链接库文件的知识和样例(有详细步骤,很清楚)

    一.DLL动态链接库文件的知识简介: Windows的发展要求允许同时运行的几个程序共享一组函数的单一拷贝.动态链接库就是在这种情况下出现的.动态链接库不用重复编译或链接,一旦装入内存,Dlls函数可 ...

  10. windows下编写dll

    dll的优点 简单的说,dll有以下几个优点: 1) 节省内存.同一个软件模块,若是以源代码的形式重用,则会被编译到不同的可执行程序中,同时运行这些exe时这些模块的二进制码会被重复加载到内存中.如果 ...

随机推荐

  1. Oracle存储过程记录异常日志

    一般我们会将一些涉及到数据库的定时任务直接用存储过程搞定,省去了后端代码的开发.部署,简单.快速,但这种方式存在一个弊端——当存储过程执行出错了,我们无法感知.解决办法也简单,学代码那样去捕获异常.打 ...

  2. Js中常用的字符串,数组,函数扩展

    由于最近辞职在家,自己的时间相对多一点.所以就根据prototytpeJS的API,结合自己正在看的司徒大神的<javascript框架设计>,整理了下Js中常用一些字符串,数组,函数扩展 ...

  3. (转)winform安装项目、安装包的制作、部署

    本文转载自:http://zhan.renren.com/cxymst?gid=3602888498037535727&from=post&checked=true 1,解决方案—添加 ...

  4. nested exception is com.mysql.jdbc.PacketTooBigException: Packet for query is too large (1109 > 1024

    MySQL的一个系统参数:max_allowed_packet >mysql -u root -p //root登录 1. 查看系统参数:show VARIABLES like '%max_al ...

  5. ActiveMQ入门之四--ActiveMQ持久化方式

    消息持久性对于可靠消息传递来说应该是一种比较好的方法,有了消息持久化,即使发送者和接受者不是同时在线或者消息中心在发送者发送消息后宕机了,在消息中心重新启动后仍然可以将消息发送出去,如果把这种持久化和 ...

  6. ALSA声卡11_从零编写之调试——学习笔记

    1.调试 (1)把程序拷贝到服务器上进行编译 (2)把程序放到内核上面去 重新配置内核,吧原来的声卡驱动程序去掉 a. 修改语法错误 11th_myalsa b. 配置内核去掉原来的声卡驱动 -> ...

  7. socket_udp客户端循环输入

    server--------------#!/usr/bin/env python # encoding: utf-8  # Date: 2018/6/7 from socket import * s ...

  8. Linux tar命令高级用法——备份数据

    Linux tar命令高级用法——备份数据 2015-12-31 Linux学习 Linux上有功能强大的tar命令,tar最初是为了制作磁带备份(tape archive)而设计的,它的作用是把文件 ...

  9. sublime text 怎么浏览包

    点击到设置里 里面有个包浏览.

  10. maven中的pom配置文件标签的详细介绍

    <span style="padding:0px; margin:0px"><project xmlns="http://maven.apache.or ...