和可执行文件一样,动态链接库也有自己的入口地址,如果系统或者当前进程的某个线程调用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. ListView-电影列表

    import React, { Component } from 'react';import { Platform, StyleSheet, Text, View, ListView } from ...

  2. vue-cli 3.5 解决 typescript cannot find file 问题。

    版本: "ts-loader": "^3.5.0","typescript": "^3.3.4000", "v ...

  3. 批量自动化生成PDF目录标签

    所需软件: 1. FreePic2Pdf(网上很容易找到) 2. python3 3. 天若OCR 链接:https://pan.baidu.com/s/1B9dUr3gc0pv0BSHo5QYGsQ ...

  4. 《大数据日知录》读书笔记-ch11大规模批处理系统

    MapReduce: 计算模型: 实例1:单词统计 实例2:链接反转 实例3:页面点击统计 系统架构: 在Map阶段还可以执行可选的Combiner操作,类似于Reduce,但是在Mapper sid ...

  5. Ubuntu下配置安装Hadoop 2.2

    ---恢复内容开始--- 这两天玩Hadoop,之前在我的Mac上配置了好长时间都没成功的Hadoop环境,今天想在win7 虚拟机下的Ubuntu12.04 64位机下配置, 然后再建一个组群看一看 ...

  6. 关于mouseleave事件触发的bug问题

    在做下拉树搜索功能的时候,下方内容框需要一个鼠标移出时就隐藏的功能,于是使用mouseleave的方法, 但是出现了一个问题就是在点击树展开个隐藏的时候,也触发了leave事件,将下方的树进行隐藏,出 ...

  7. 【.Net】 【C++】容器类型对照

    C# 中主要有两类容器:一个是 System.Array 类(参阅:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/c ...

  8. Codeforces 936E. Iqea

    Description 给出一张四连通网格图,其中有 \(n\) 个点是连通的,维护以下两种操作: 1.把某个点变黑 2.给出一个白点,查询离这个白点最近的黑点的距离 题面 Solution 我们把每 ...

  9. Nginx通关攻略

    Nginx是什么 没有听过Nginx?不要紧,一定听过它的"同行"Apache吧!Nginx同Apache一样都是一种WEB服务器.基于REST架构风格,以统一资源描述符(Unif ...

  10. js设置下拉框选中后change事件无效解决

    下拉框部分代码: <select id="bigType"> <option value="">请选择</option> & ...