和可执行文件一样,动态链接库也有自己的入口地址,如果系统或者当前进程的某个线程调用LoadLibrary函数加载或者使用FreeLibrary卸载该动态链接库的时候,会自动使用3个特定的堆栈参数跳转到该地址来运行。入口函数是为了完成动态链接库代码的初始化和善后工作,比如卸载后的资源释放。

  这三个参数具有特殊的含义。

  BOOL APIENTRY DllMain(

    HMODULE hModule,

    DWORD ul_reason_for_call,

    LPVOID lpReserved
  )


  第一个参数是实例句柄,这个所谓的句柄实际上就是该加载进程在内存中的景象地址,注意进程句柄和动态链接库的模块句柄是不一样的。比如,如果用户想使用hModule去加载该动态链接库中的一个位图资源,那将永远得不到结果。动态链接库的模块句柄依附于加载进程,脱离进程独立于系统空间中的动态链接库是不存在的,也没有意义。动态链接库总是要加载到进程中的某个地址,该地址就是动态链接库的模块句柄,这个句柄不同于进程实例句柄,它是相对于进程入口地址的一个偏移量,只有通过模块句柄才能寻址到该动态链接库中嵌套的各种窗口或者其他资源。

  ul_reason_for_call 参数表示该动态链接库是在什么条件下被加载的,即加载的原因。当用户显式采用LoadLibrary(Ex)函数加载一个动态链接库或者由进程本身隐式加载该库时,该入口函数就会被调用,入口参数 ul_reason_for_call 这时就等于DLL_PROCESS_ATTACH,入口函数的返回值就是LoadLibrary函数的返回值,它们都是通过EAX寄存器来传递的。根据LoadLibrary(Ex)函数的说明,这个函数如果返回一个NULL值,表示加载失败。实际上在DllMain 函数中,只要返回FALSE,LoadLibrary(Ex)函数就会返回一个NULL值。如果动态链接库需要排他调用,可以在入口点函数中判断加载进程的文件名,或者履行其他合法性检查;如果不希望被某个进程调用,可以直接在DllMain函数中返回FALSE。DLL_PROCESS_ATTACH分支往往用于实现系统初始化,比如建立数据库链接,创建钩子函数,分配系统资源,保存入口进程实例句柄等。

  同样,当进程不再使用该动态链接库,比如调用ExitProcess函数或者显式调用FreeLibrary函数时,系统又会使用DLL_PROCESS_DETACH参数执行相关分支。用于释放系统资源、断开数据库链接、卸载钩子函数、关闭文件、释放内存等。

  注意:DLL_PROCESS_DETACH分支的执行是有条件的,进程的意外终止,并不会执行DLL_PROCESS_DETACH分支语句,这样分支中的关闭资源、断开链接、关闭文件等善后语句就无法执行,这将会造成一些数据的潜在丢失。因此除非万不得已,不要轻易使用TerminateProcess函数终止进程执行。

  如果线程创建时DLL完成到进程空间的映射,这样系统会使用DLL_THREAD_ATTACH进入入口点。而系统执行ExitThread函数时会执行DLL_THREAD_DETACH分支。同样的规则适用于线程,用户不要轻易使用TerminateThread函数,这也会导致一些不可预知的内存泄漏或者资源没有释放和数据丢失。

  如果用户不在乎DLL_THREAD_DETACH和DLL_THREAD_ATTACH通知,又希望提高创建和撤销线程的性能,可以在收到DLL_THREAD_ATTACH通知时,调用DisableThreadLibraryCalls函数。

  注意:关于lpReserved参数----在静态(隐式)加载和调用LoadLibray函数实现动态(显式)加载动态库时,这两种情况是不一样的,动态加载时这个值为0。如果用户希望自己编写的的动态链接库只能被动态加载或者只能被静态加载,可以通过判断这个参数来实现。

代码示例:

dllmain.cpp(生成mydll.dll):

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include<stdio.h>
#include<Psapi.h>
#include<Windows.h>
typedef void(*pFnPtr)(char*); BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{ char szName[MAX_PATH];
if (lpReserved != )
{
//只允许动态加载,静态加载将提示错误并退出
MessageBox(NULL, "只允许动态加载", "Sorry", MB_OK);
return FALSE;
}
GetModuleBaseName(GetCurrentProcess(),NULL ,szName,MAX_PATH);//获取当前主进程的base name
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
//故意将调用进程的名字改为test.exe,该动态链接库将无法加载。
if (strcmp(szName, "test.exe") == )
return FALSE;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
} extern "C"
{
//主调进程将调用该函数,该函数再调用主调进程的ExeFn函数
_declspec(dllexport) int fnImportingDLL()
{
MessageBox(NULL, "Dll Function called!", "mydll", MB_OK);
pFnPtr fn = (pFnPtr)::GetProcAddress(GetModuleHandle(NULL), "ExeFn");
if (fn)
fn("梦回吹角连营");
else
{
MessageBox(NULL, "It Did not work:", "From DLL", MB_OK);
return -;
}
}
}

main.cpp(生成console.exe):

#include<iostream>
#include<Windows.h>
#define _DYNAMIC_
#ifndef _DYNAMIC_
#pragma comment(lib,"mydll.lib") //静态加载
extern "C" _declspec(dllexport) int fnImportingDLL();
#endif
typedef int(*pfnImportingDLL)(); using namespace std;
int main(int argc, char* argv[])
{
#ifdef _DYNAMIC_ //动态加载
pfnImportingDLL fnImportingDLL = NULL;
HMODULE hModule=::LoadLibrary("mydll.dll");
if (hModule == NULL)
{
cout << "无法加载mydll.dll" << endl;
return -;
}
fnImportingDLL =(pfnImportingDLL) GetProcAddress(hModule, "fnImportingDLL");
if (fnImportingDLL == NULL)
{
cout << "找不到fnImportingDLL函数" << endl;
return -;
}
#endif
cout << "I'm going...\n";
fnImportingDLL();
cout << "Game Over\n";
#ifdef _DYNAMIC_
::FreeLibrary(hModule);
#endif
return ;
} extern "C"
{
//该函数将有动态链接库中的函数来调用
_declspec(dllexport) void ExeFn(char* lpszMessage)
{
MessageBox(NULL, lpszMessage, "From Exe", MB_OK);
}
}

将生成的 console.exe文件和mydll.dll文件放在同一个文件夹下

注释掉 _DYNAMIC_ 宏将静态加载mydll.dll,这样会弹出对话框提示只能动态加载,因为在DllMain中检查了lpReserved的值并做了判断。

将console.exe的名字改为test.exe,然后在cmd中运行test.exe,将显示无法加载mydll.dll。

Windows API 编程-----DLL编程之禁止加载自己的更多相关文章

  1. 使用Windows API进行串口编程

    使用Windows API进行串口编程   串口通信一般分为四大步:打开串口->配置串口->读写串口->关闭串口,还可以在串口上监听读写等事件.1.打开和关闭串口Windows中串口 ...

  2. dll的两种加载方式(pend)+ delayload

    看过关于动态库的调用例子,于是决定动手做一做:dll的对外接口声明头文件,Mydll.h: //Mydll.h #include <stdio.h> #include <stdlib ...

  3. ArcGIS API for Silverlight 调用GP服务加载等值线图层

    原文:ArcGIS API for Silverlight 调用GP服务加载等值线图层 第二篇.Silverlight客户端调用GP服务 利用ArcGIS API for Silverlight实现G ...

  4. [转] 从 dll 程序集中动态加载窗体

    无涯 原文 从 dll 程序集中动态加载窗体 [原创] 昨天晚上花了一晚上时间写了一个从程序集中动态加载窗体的程序.将任何包含窗体的代码编译成 dll 文件,再把 dll 文件拷贝到本程序的目录下,本 ...

  5. Windows -- 从注册表删除IE浏览器加载项

    Windows -- 从注册表删除IE浏览器加载项 1.  一部分加载项从注册表以下位置直接删除 2.  一部分扩展项从注册表以下位置直接删除

  6. selenium chrome.options禁止加载图片和js

    #新建一个选项卡 from selenium import webdriver options = webdriver.ChromeOptions() #禁止加载图片 prefs = { 'profi ...

  7. 基于C++简单Windows API的socket编程(阻塞模式)

    1. 概述:简单的基于Windows API的socket点对点聊天程序,为了方便初学者,本文代码均采用阻塞原理编写. 2. 代码样例 Server.cpp(服务端) #include <cst ...

  8. Scala函数式编程(六) 懒加载与Stream

    前情提要 Scala函数式编程指南(一) 函数式思想介绍 scala函数式编程(二) scala基础语法介绍 Scala函数式编程(三) scala集合和函数 Scala函数式编程(四)函数式的数据结 ...

  9. 学习:Windows API核心DLL文件

    在 Windows 的系统目录中,存在着很多的动态链接库文件(DLL 文件).这些 DLL 文件中包括了 Windows API 函数可执行程序. DLL 将各函数"导出",这样应 ...

随机推荐

  1. centOS7虚拟机设置固定IP

      说明:想要达到的如下效果, 1.笔记本主机IP为设置自动获取,不管什么情况下,不受虚拟机影响,只要连接外网就可以正常上网: 2.只要笔记本主机可以正常访问外网,启动虚拟机中的CentOS 7系统就 ...

  2. Angular material mat-icon 资源参考_Navigation

    ul,li>ol { margin-bottom: 0 } dt { font-weight: 700 } dd { margin: 0 1.5em 1.5em } img { height: ...

  3. stark - 1 ⇲一些理念

    ⒈.django项目启动时,自定义执行某个py文件. 在任意的app的apps.py中的Config类中定义ready方法,并调用autodiscover_modules from django.ap ...

  4. vue-lazyload图片懒加载的简单使用

    一.vue lazyload插件: 插件地址:https://github.com/hilongjw/vue-lazyload demo:http://hilongjw.github.io/vue-l ...

  5. python爬虫之urllib库(三)

    python爬虫之urllib库(三) urllib库 访问网页都是通过HTTP协议进行的,而HTTP协议是一种无状态的协议,即记不住来者何人.举个栗子,天猫上买东西,需要先登录天猫账号进入主页,再去 ...

  6. hadoop job -kill 和 yarn application -kill 区别

    hadoop job -kill 调用的是CLI.java里面的job.killJob(); 这里会分几种情况,如果是能查询到状态是RUNNING的话,是直接向AppMaster发送kill请求的.Y ...

  7. ajax session timeout

    授权过期后AJAX操作跳转到登录页的一种全局处理方式 菜鸟程序员之Asp.net MVC Session过期异常的处理 基于WebImage的图片上传工具类

  8. BottomSheetDialogFragment 如何设置高度和禁止滑动

    主要是获取dialog 的BottomSheetBehavior 然后设置 setPeekHeight 和 BottomSheetCallback. private BottomSheetBehavi ...

  9. Missy

    毕业两年了,非常怀念大学生活.有时间还想回去看看,为什么我的眼里常含着泪水,因为我对这片土地爱的深沉. 你问我大学时最后悔的事情是什么,可能是没有对她说 I like you 吧,至今偶尔也会想起她. ...

  10. 关于JS闭包

    今天在敲代码的时候,发现很多JQ插件在写闭包的时候都会用到下面的写法: (function ($) { ... })(jQuery); 一时的好奇心驱使,我研究起了这一写法来.大家都知道,在 $ 没有 ...