编写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时这些模块的二进制码会被重复加载到内存中.如果 ...
随机推荐
- java创建多线程&创建进程
概述 并发和并行是即相似又有区别: 并行:指两个或多个事件在同一时刻发生: 并发:指两个或多个事件在同一时间段内发生. 进程是指一个内存中运行中的应用程序.每个进程都有自己独立的一块内存空间,一个应用 ...
- ballerina 学习九 Client endpoints
说白了就是连接外部服务的,可以是http jms websocket .... 简单例子 代码 import ballerina/http; import ballerina/log; endpoin ...
- Hbase 之 HBase 的整体架构
HBase 系统架构图 组成部件说明 Client: 使用HBase RPC机制与HMaster和HRegionServer进行通信 Client与HMaster进行通信进行管理类操作 Cli ...
- 项目代码部署百度云(使用git部署,node环境)
学习做了一个小demo,总是在自己的电脑,和局域网上运行很没意思,现在就做点有意思的事情,将代码部署百度云. 1)首先你得进入百度云(直接百度,先要注册一个账号) 2)点击那个“应用引擎”,就会进入 ...
- golang的sync包例子
package main import ( "fmt" "sync" ) var wg sync.WaitGroup func asyncTestFunc() ...
- java代码--------构造方法的调用
总结: package com.sads; //构造方法何时被调用, //构造方法里的内容先执行 public class Sdw { static { System.out.println(&quo ...
- Solr入门和实践以及我对Solr的8点理解
友情提示Solr的内容还是比较多的,一篇文章只能讲解一部分.全面介绍,没兴趣,没时间,也没能力,回报还不大.本文只写点我认为比较重要的知识点,独特的个人想法.仅供参考哦,更多细节需要自己去琢磨. 概述 ...
- java实现二叉树demo
二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的.分别称作这个根的左子树和右子树的二叉树组成. 这个定义是递归的.由于左.右子 ...
- 显示本月日历demo
import java.text.DateFormatSymbols; import java.util.Calendar; import java.util.GregorianCalendar; p ...
- Java线程状态切换以及核心方法
1.Java线程状态 1.1 线程主要状态 ①初始(NEW):新创建了一个线程对象,但还没有调用start()方法.②运行(RUNNABLE):Java线程中将就绪(ready)和运行中(runnin ...