VS2015项目中,MFC内存中调用DLL函数(VC6生成的示例DLL)

本例主要讲一下,用VC6如何生成DLL,用工具WinHex取得DLL全部内容,VC2015项目加载内存中的DLL函数,并调用函数的示例。        

本例中的示例代码下载,点击可以下载

一、VC++6.0生成示例DLL项目

1.新建项目,这里选择Win32 Dynamic-link Library,如下图:

 2.选择“A simple DLL project”,然后点击完成,如下:

  3.生成示例项目后,如下图:

4.添加自己的示例函数,这里以简单的求和函数为例:

/*
程序功能:DLL生成项目,生成测试的DLL文件,
作者:依星
QQ:34596561,312337667
日期:2023/8/15
*/
#include "stdafx.h"extern "C" __declspec(dllexport) int sum(int a,int b);BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{return TRUE;
}extern "C" __declspec(dllexport) int sum(int a,int b)
{return a+b;
}

5.设置项目为release,开始编译构建DLL文件,按“F7”。

二、用WinHex复制取得DLL文件的全部数据

这里使用WinHex来取得DLL的全部数据,具体使用流程如下:

1.打开WinHex,然后将DLL文件拖入到主界面中,如下图:

 2.选择“编辑”---“复制所有”---“C源码”,这里实际是复制了所有的数据,如下图:

3.新建一个记事本,命名为“dll.h” ,将上面复制的数据粘贴到记事本中,然后将此文件复制到VC2015项目中。

 三、建立VC2015示例工程,调用CMemLoadDll类源码

1. 这里先用VC2015建立一个标准的MFC工程项目,新建---项目,如下:

2. 选择MFC--MFC应用程序,然后选择保存的目录,并命名项目名称,如下图:

 3.下一步,然后再选择“基于对话框”,点击“在静态库中使用MFC”,如下图:

 4.至此没有特别要设置的了,点击下一步,直到完成。

5.项目创建后,默认为Unicode字符集,这里改为多字节;(PS:其实改不改都行,主要是项目中都是使用的多字节,习惯了)如下图:

6.将上一步生成的Dll.h文件添加到解决方案的头文件中,然后再新建一个头文件和源文件,用于把网上的CMemLoadDll类源码复制过来,源码将在后面贴出来,咱们先说流程。添加好之后,如下图工程目录:

7.在主对话框中,加一个按钮,用于调用示例:

8.双击按钮,显示点击方法(MemRunDllDlg.cpp): 

/*
调用CMemLoadDll,
加载内存中的DLL,
并运行DLL中的函数
//
dllData:为生成的测试DLL数据文件dll.h;(注:此处也可以把这个DLL文件加到资源中加载,或者以文件形式读取到内存中,都是可以的)
为安全起见,可以把DLL的数据加密存储到DLL.H中,本例不再展示。
*/
void CMemRunDllDlg::OnBnClickedButton1()
{// TODO: 在此添加控件通知处理程序代码CMemLoadDll *pMemLoadDll = new CMemLoadDll();if (!pMemLoadDll->MemLoadLibrary(&dllData, sizeof(dllData))) //加载dll到当前进程的地址空间{AfxMessageBox("Load DLL error!");return ;}addNumberProc addNumber = (addNumberProc)pMemLoadDll->MemGetProcAddress("sum");if (addNumber == NULL){AfxMessageBox("Find Add function failed!");return ;}int c = addNumber(1, 2);char itc[10];sprintf(itc, "%d", c);AfxMessageBox(itc);}

9 .在此文件头部(MemRunDllDlg.cpp),加入文件引用及DLL函数声明,如下:

#include "dll.h"
#include "MemLoadDll.h"typedef int(*addNumberProc)(int, int);

10.生成EXE并运行,正常运行,如下:

 四、CMemLoadDll类源码,网上搜索的,作者不详

1.MemLoadDll.h头文件

#pragma once  typedef   BOOL(__stdcall *ProcDllMain)(HINSTANCE, DWORD, LPVOID);class CMemLoadDll
{
public:CMemLoadDll();~CMemLoadDll();BOOL    MemLoadLibrary(void *lpFileData, int DataLength);  // Dll file data buffer  FARPROC MemGetProcAddress(LPCSTR lpProcName);
private:BOOL isLoadOk;BOOL CheckDataValide(void *lpFileData, int DataLength);int  CalcTotalImageSize();void CopyDllDatas(void *pDest, void *pSrc);BOOL FillRavAddress(void *pBase);void DoRelocation(void *pNewBase);int  GetAlignedSize(int Origin, int Alignment);
private:ProcDllMain pDllMain;private:DWORD  pImageBase;PIMAGE_DOS_HEADER pDosHeader;PIMAGE_NT_HEADERS pNTHeader;PIMAGE_SECTION_HEADER pSectionHeader;};

2.MemLoadDll.cpp源文件

#include "stdafx.h"
#include <windows.h>  
#include <assert.h>  
#include "MemLoadDll.h"  CMemLoadDll::CMemLoadDll()
{isLoadOk = FALSE;pImageBase = NULL;pDllMain = NULL;
}CMemLoadDll::~CMemLoadDll()
{if (isLoadOk){assert(pImageBase != NULL);assert(pDllMain != NULL);//脱钩,准备卸载dll  pDllMain((HINSTANCE)pImageBase, DLL_PROCESS_DETACH, 0);VirtualFree((LPVOID)pImageBase, 0, MEM_RELEASE);}
}//MemLoadLibrary函数从内存缓冲区数据中加载一个dll到当前进程的地址空间,缺省位置0x10000000  
//返回值: 成功返回TRUE , 失败返回FALSE  
//lpFileData: 存放dll文件数据的缓冲区  
//DataLength: 缓冲区中数据的总长度  
BOOL CMemLoadDll::MemLoadLibrary(void *lpFileData, int DataLength)
{if (pImageBase != NULL){return FALSE;  //已经加载一个dll,还没有释放,不能加载新的dll  }//检查数据有效性,并初始化  if (!CheckDataValide(lpFileData, DataLength)){return FALSE;}//计算所需的加载空间  int ImageSize = CalcTotalImageSize();if (ImageSize == 0){return FALSE;}// 分配虚拟内存  void *pMemoryAddress = VirtualAlloc((LPVOID)NULL, ImageSize,MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if (pMemoryAddress == NULL){return FALSE;}else{CopyDllDatas(pMemoryAddress, lpFileData); //复制dll数据,并对齐每个段  //重定位信息  if (pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress > 0&& pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size > 0){DoRelocation(pMemoryAddress);}//填充引入地址表  if (!FillRavAddress(pMemoryAddress))  //修正引入地址表失败  {VirtualFree(pMemoryAddress, 0, MEM_RELEASE);return FALSE;}//修改页属性。应该根据每个页的属性单独设置其对应内存页的属性。这里简化一下。  //统一设置成一个属性PAGE_EXECUTE_READWRITE  unsigned long old;VirtualProtect(pMemoryAddress, ImageSize, PAGE_EXECUTE_READWRITE, &old);}//修正基地址  pNTHeader->OptionalHeader.ImageBase = (DWORD)pMemoryAddress;//接下来要调用一下dll的入口函数,做初始化工作。  pDllMain = (ProcDllMain)(pNTHeader->OptionalHeader.AddressOfEntryPoint + (DWORD)pMemoryAddress);BOOL InitResult = pDllMain((HINSTANCE)pMemoryAddress, DLL_PROCESS_ATTACH, 0);if (!InitResult)  //初始化失败  {pDllMain((HINSTANCE)pMemoryAddress, DLL_PROCESS_DETACH, 0);VirtualFree(pMemoryAddress, 0, MEM_RELEASE);pDllMain = NULL;return FALSE;}isLoadOk = TRUE;pImageBase = (DWORD)pMemoryAddress;return TRUE;
}//MemGetProcAddress函数从dll中获取指定函数的地址  
//返回值: 成功返回函数地址 , 失败返回NULL  
//lpProcName: 要查找函数的名字或者序号  
FARPROC  CMemLoadDll::MemGetProcAddress(LPCSTR lpProcName)
{if (pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0 ||pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0){return NULL;}if (!isLoadOk){return NULL;}DWORD OffsetStart = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;DWORD Size = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pImageBase + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);int iBase = pExport->Base;int iNumberOfFunctions = pExport->NumberOfFunctions;int iNumberOfNames = pExport->NumberOfNames; //<= iNumberOfFunctions  LPDWORD pAddressOfFunctions = (LPDWORD)(pExport->AddressOfFunctions + pImageBase);LPWORD  pAddressOfOrdinals = (LPWORD)(pExport->AddressOfNameOrdinals + pImageBase);LPDWORD pAddressOfNames = (LPDWORD)(pExport->AddressOfNames + pImageBase);int iOrdinal = -1;if (((DWORD)lpProcName & 0xFFFF0000) == 0)  //IT IS A ORDINAL!  {iOrdinal = (DWORD)lpProcName & 0x0000FFFF - iBase;}else     //use name  {int iFound = -1;for (int i = 0; i < iNumberOfNames; i++){char *pName = (char *)(pAddressOfNames[i] + pImageBase);if (strcmp(pName, lpProcName) == 0){iFound = i;break;}}if (iFound >= 0){iOrdinal = (int)(pAddressOfOrdinals[iFound]);}}if (iOrdinal < 0 || iOrdinal >= iNumberOfFunctions){return NULL;}else{DWORD pFunctionOffset = pAddressOfFunctions[iOrdinal];if (pFunctionOffset > OffsetStart && pFunctionOffset < (OffsetStart + Size)) //maybe Export Forwarding  {return NULL;}else{return (FARPROC)(pFunctionOffset + pImageBase);}}}// 重定向PE用到的地址  
void CMemLoadDll::DoRelocation(void *NewBase)
{/* 重定位表的结构:// DWORD sectionAddress, DWORD size (包括本节需要重定位的数据)// 例如 1000节需要修正5个重定位数据的话,重定位表的数据是// 00 10 00 00   14 00 00 00      xxxx xxxx xxxx xxxx xxxx 0000// -----------   -----------      ----// 给出节的偏移  总尺寸=8+6*2     需要修正的地址           用于对齐4字节// 重定位表是若干个相连,如果address 和 size都是0 表示结束// 需要修正的地址是12位的,高4位是形态字,intel cpu下是3*///假设NewBase是0x600000,而文件中设置的缺省ImageBase是0x400000,则修正偏移量就是0x200000  DWORD Delta = (DWORD)NewBase - pNTHeader->OptionalHeader.ImageBase;//注意重定位表的位置可能和硬盘文件中的偏移地址不同,应该使用加载后的地址  PIMAGE_BASE_RELOCATION pLoc = (PIMAGE_BASE_RELOCATION)((unsigned long)NewBase+ pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);while ((pLoc->VirtualAddress + pLoc->SizeOfBlock) != 0)  //开始扫描重定位表  {WORD *pLocData = (WORD *)((int)pLoc + sizeof(IMAGE_BASE_RELOCATION));//计算本节需要修正的重定位项(地址)的数目  int NumberOfReloc = (pLoc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);for (int i = 0; i < NumberOfReloc; i++){if ((DWORD)(pLocData[i] & 0xF000) == 0x00003000)  //这是一个需要修正的地址  {// 举例:  // pLoc->VirtualAddress = 0x1000;  // pLocData[i] = 0x313E; 表示本节偏移地址0x13E处需要修正  // 因此 pAddress = 基地址 + 0x113E  // 里面的内容是 A1 ( 0c d4 02 10)  汇编代码是: mov eax , [1002d40c]  // 需要修正1002d40c这个地址  DWORD *pAddress = (DWORD *)((unsigned long)NewBase + pLoc->VirtualAddress + (pLocData[i] & 0x0FFF));*pAddress += Delta;}}//转移到下一个节进行处理  pLoc = (PIMAGE_BASE_RELOCATION)((DWORD)pLoc + pLoc->SizeOfBlock);}
}//填充引入地址表  
BOOL CMemLoadDll::FillRavAddress(void *pImageBase)
{// 引入表实际上是一个 IMAGE_IMPORT_DESCRIPTOR 结构数组,全部是0表示结束  // 数组定义如下:  //  // DWORD   OriginalFirstThunk;         // 0表示结束,否则指向未绑定的IAT结构数组  // DWORD   TimeDateStamp;  // DWORD   ForwarderChain;             // -1 if no forwarders  // DWORD   Name;                       // 给出dll的名字  // DWORD   FirstThunk;                 // 指向IAT结构数组的地址(绑定后,这些IAT里面就是实际的函数地址)  int i;unsigned long Offset = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;if (Offset == 0){return TRUE;    //No Import Table  }PIMAGE_IMPORT_DESCRIPTOR pID = (PIMAGE_IMPORT_DESCRIPTOR)((unsigned long)pImageBase + Offset);while (pID->Characteristics != 0){PIMAGE_THUNK_DATA pRealIAT = (PIMAGE_THUNK_DATA)((unsigned long)pImageBase + pID->FirstThunk);PIMAGE_THUNK_DATA pOriginalIAT = (PIMAGE_THUNK_DATA)((unsigned long)pImageBase + pID->OriginalFirstThunk);//获取dll的名字  TCHAR buf[256]; //dll name;  BYTE *pName = (BYTE *)((unsigned long)pImageBase + pID->Name);for (i = 0; i < 256; i++){if (pName[i] == 0){break;}buf[i] = pName[i];}if (i >= 256){return FALSE;    // bad dll name  }else{buf[i] = 0;}HMODULE hDll = GetModuleHandle(buf);if (hDll == NULL){hDll = LoadLibrary(buf);}if (hDll == NULL){return FALSE;    //NOT FOUND DLL  }//获取DLL中每个导出函数的地址,填入IAT  //每个IAT结构是 :  // union { PBYTE  ForwarderString;  //   PDWORD Function;  //   DWORD Ordinal;  //   PIMAGE_IMPORT_BY_NAME  AddressOfData;  // } u1;  // 长度是一个DWORD ,正好容纳一个地址。  for (i = 0; ; i++){if (pOriginalIAT[i].u1.Function == 0){break;}FARPROC lpFunction = NULL;if (pOriginalIAT[i].u1.Ordinal & IMAGE_ORDINAL_FLAG)  //这里的值给出的是导出序号  {lpFunction = GetProcAddress(hDll, (LPCSTR)(pOriginalIAT[i].u1.Ordinal & 0x0000FFFF));}else     //按照名字导入  {//获取此IAT项所描述的函数名称  PIMAGE_IMPORT_BY_NAME pByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pImageBase + (DWORD)(pOriginalIAT[i].u1.AddressOfData));//    if(pByName->Hint !=0)  //     lpFunction = GetProcAddress(hDll, (LPCSTR)pByName->Hint);  //    else  lpFunction = GetProcAddress(hDll, (char *)pByName->Name);}if (lpFunction != NULL)  //找到了!  {pRealIAT[i].u1.Function = (DWORD)lpFunction;//(PDWORD) lpFunction;  }else{return FALSE;}}//move to next  pID = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pID + sizeof(IMAGE_IMPORT_DESCRIPTOR));}return TRUE;
}//CheckDataValide函数用于检查缓冲区中的数据是否有效的dll文件  
//返回值: 是一个可执行的dll则返回TRUE,否则返回FALSE。  
//lpFileData: 存放dll数据的内存缓冲区  
//DataLength: dll文件的长度  
BOOL CMemLoadDll::CheckDataValide(void *lpFileData, int DataLength)
{//检查长度  if (DataLength < sizeof(IMAGE_DOS_HEADER)){return FALSE;}pDosHeader = (PIMAGE_DOS_HEADER)lpFileData;  // DOS头  //检查dos头的标记  if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE){return FALSE;    //0x5A4D : MZ  }//检查长度  if ((DWORD)DataLength < (pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS))){return FALSE;}//取得pe头  pNTHeader = (PIMAGE_NT_HEADERS)((unsigned long)lpFileData + pDosHeader->e_lfanew); // PE头  //检查pe头的合法性  if (pNTHeader->Signature != IMAGE_NT_SIGNATURE){return FALSE;    //0x00004550 : PE00  }if ((pNTHeader->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) //0x2000  : File is a DLL  {return FALSE;}if ((pNTHeader->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0) //0x0002 : 指出文件可以运行  {return FALSE;}if (pNTHeader->FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER)){return FALSE;}//取得节表(段表)  pSectionHeader = (PIMAGE_SECTION_HEADER)((int)pNTHeader + sizeof(IMAGE_NT_HEADERS));//验证每个节表的空间  for (int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++){if ((pSectionHeader[i].PointerToRawData + pSectionHeader[i].SizeOfRawData) >(DWORD)DataLength){return FALSE;}}return TRUE;
}//计算对齐边界  
int CMemLoadDll::GetAlignedSize(int Origin, int Alignment)
{return (Origin + Alignment - 1) / Alignment * Alignment;
}//计算整个dll映像文件的尺寸  
int CMemLoadDll::CalcTotalImageSize()
{int Size;if (pNTHeader == NULL){return 0;}int nAlign = pNTHeader->OptionalHeader.SectionAlignment; //段对齐字节数  // 计算所有头的尺寸。包括dos, coff, pe头 和 段表的大小  Size = GetAlignedSize(pNTHeader->OptionalHeader.SizeOfHeaders, nAlign);// 计算所有节的大小  for (int i = 0; i < pNTHeader->FileHeader.NumberOfSections; ++i){//得到该节的大小  int CodeSize = pSectionHeader[i].Misc.VirtualSize;int LoadSize = pSectionHeader[i].SizeOfRawData;int MaxSize = (LoadSize > CodeSize) ? (LoadSize) : (CodeSize);int SectionSize = GetAlignedSize(pSectionHeader[i].VirtualAddress + MaxSize, nAlign);if (Size < SectionSize){Size = SectionSize;    //Use the Max;  }}return Size;
}
//CopyDllDatas函数将dll数据复制到指定内存区域,并对齐所有节  
//pSrc: 存放dll数据的原始缓冲区  
//pDest:目标内存地址  
void CMemLoadDll::CopyDllDatas(void *pDest, void *pSrc)
{// 计算需要复制的PE头+段表字节数  int  HeaderSize = pNTHeader->OptionalHeader.SizeOfHeaders;int  SectionSize = pNTHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);int  MoveSize = HeaderSize + SectionSize;//复制头和段信息  memmove(pDest, pSrc, MoveSize);//复制每个节  for (int i = 0; i < pNTHeader->FileHeader.NumberOfSections; ++i){if (pSectionHeader[i].VirtualAddress == 0 || pSectionHeader[i].SizeOfRawData == 0){continue;}// 定位该节在内存中的位置  void *pSectionAddress = (void *)((unsigned long)pDest + pSectionHeader[i].VirtualAddress);// 复制段数据到虚拟内存  memmove((void *)pSectionAddress,(void *)((DWORD)pSrc + pSectionHeader[i].PointerToRawData),pSectionHeader[i].SizeOfRawData);}//修正指针,指向新分配的内存  //新的dos头  pDosHeader = (PIMAGE_DOS_HEADER)pDest;//新的pe头地址  pNTHeader = (PIMAGE_NT_HEADERS)((int)pDest + (pDosHeader->e_lfanew));//新的节表地址  pSectionHeader = (PIMAGE_SECTION_HEADER)((int)pNTHeader + sizeof(IMAGE_NT_HEADERS));return;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/43092.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL Server Express 自动备份方案

文章目录 SQL Server Express 自动备份方案前言方案原理SQL Server Express 自动备份1.创建存储过程2.设定计划任务3.结果检查sqlcmd 参数说明SQL Server Express 自动备份方案 前言 对于许多小型企业和个人开发者来说,SQL Server Express是一个经济实惠且强大的数据库解决方…

Spring Framework中的Bean生命周期

目录 一.Bean生命周期的简介 1.基本概念 2.Spring生命周期的几大阶段 3.注意点及小结 4.生活案例 5.Spring容器管理JavaBean的初始化过程 二. Bean的单例选择与多例选择 1.单例选择与多例选择的优缺点 1.1单例模式的优点&#xff1a; 1.2单例模式的缺点&#xff1a; 1…

JDK 8 升级 JDK 17 全流程教学指南

JDK 8 升级 JDK 17 首先已有项目升级是会经历一个较长的调试和自测过程来保证允许和兼容没有问题。先说几个重要的点 遇到问题别放弃仔细阅读报错&#xff0c;精确到每个单词每一行&#xff0c;不是自己项目的代码也要点进去看看源码到底是为啥报错明确你项目引入的包&#x…

第三届“赣政杯”网络安全大赛 | 赛宁筑牢安全应急防线

​​为持续强化江西省党政机关网络安全风险防范意识&#xff0c;提高信息化岗位从业人员基础技能&#xff0c;提升应对网络安全风险处置能力。由江西省委网信办、江西省发展改革委主办&#xff0c;江西省大数据中心、国家计算机网络与信息安全管理中心江西分中心承办&#xff0…

Qt扫盲-QTableView理论总结

QTableView理论总结 一、概述二、导航三、视觉外观四、坐标系统五、示例代码1. 性别代理2. 学生信息模型3. 对应视图 一、概述 QTableView实现了一个tableview 来显示model 中的元素。这个类用于提供之前由QTable类提供的标准表&#xff0c;但这个是使用Qt的model/view架构提供…

MySQL 存储过程

create procedure 存储过程名 &#xff08;in | out | INPUT 参数名 参数类型&#xff0c;。。。&#xff09; 【characteristics 。。。】begin存储过程体end存储过程的参数类型 IN 、OUT、INPUT 都可以在一个存储过程带多个 没有参数&#xff08;无参数无返回&#xff09;仅…

边缘网络的作用及管理工具

自从引入软件即服务 &#xff08;SaaS&#xff09; 以来&#xff0c;它一直引领着全球按需软件部署创新的竞赛&#xff0c;它提供的灵活性以及其云计算架构带来的易于集成使其成为交付业务应用程序的标准。 在 SaaS 模型中&#xff0c;最佳用户体验的三重奏涉及无缝设置、低延…

JMeter 特殊组件-逻辑控制器与BeanShell PreProcessor 使用示例

文章目录 前言JMeter 特殊组件-逻辑控制器与BeanShell PreProcessor 使用示例1. 逻辑控制器使用1.1. While Controller 使用示例1.2. 如果&#xff08;If&#xff09;控制器 使用示例 2. BeanShell PreProcessor 使用示例 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞…

Java课题笔记~ SpringBoot简介

1. 入门案例 问题导入 SpringMVC的HelloWord程序大家还记得吗&#xff1f; SpringBoot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化Spring应用的初始搭建以及开发过程 原生开发SpringMVC程序过程 1.1 入门案例开发步骤 ①&#xff1a;创建新模块&#…

设计模式-过滤器模式(使用案例)

过滤器模式&#xff08;Filter Pattern&#xff09;或标准模式&#xff08;Criteria Pattern&#xff09;是一种设计模式&#xff0c;这种模式允许开发人员使用不同的标准来过滤一组对象&#xff0c;通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式…

服务器安装centos7踩坑

1、制作启动工具 下载iso https://developer.aliyun.com/mirror/?spma2c6h.25603864.0.0.20387abbo2RFbn http://mirrors.aliyun.com/centos/7.9.2009/isos/x86_64/?spma2c6h.25603864.0.0.1995f5ad4AhJaW下载 UltraISO https://cn.ultraiso.net/插入u盘启动 到了如图所示页面…

nginx php-fpm安装配置

nginx php-fpm安装配置 nginx本身不能处理PHP&#xff0c;它只是个web服务器&#xff0c;当接收到请求后&#xff0c;如果是php请求&#xff0c;则发给php解释器处理&#xff0c;并把结果返回给客户端。 nginx一般是把请求发fastcgi管理进程处理&#xff0c;fascgi管理进程选…

架构演进及常用架构

1架构演进及常用架构 1.1单体分层架构 1.2 多应用微服务架构 1.3 分布式集群部署 部署 CDN 节点&#xff1a; 用户访问量的增加意味着用户地域的分散请求&#xff0c;如果所有请求都直接发送中心服务器的话&#xff0c;距离越远&#xff0c;响应速度越差&#xff0c;这时就需…

【编织时空四:探究顺序表与链表的数据之旅】

本章重点 链表的分类 带头双向循环链表接口实现 顺序表和链表的区别 缓存利用率参考存储体系结构 以及 局部原理性。 一、链表的分类 实际中链表的结构非常多样&#xff0c;以下情况组合起来就有8种链表结构&#xff1a; 1. 单向或者双向 2. 带头或者不带头 3. 循环或者非…

yolov5封装进ros系统

一&#xff0c;要具备ROS环境 ROS环境搭建可以参考我之前的文章 ROS参考文章1 ROS参考文章2   建立ROS工作空间 ROS系统由自己的编译空间规则。 cd 你自己想要的文件夹&#xff08;我一般是home目录&#xff09; mkdir -p (你自己的文件夹名字&#xff0c;比如我是yolov5…

C++的stack和queue+优先队列

文章目录 什么是容器适配器底层逻辑为什么选择deque作为stack和queue的底层默认容器优先队列优先队列的模拟实现stack和queue的模拟实现 什么是容器适配器 适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总 结)&#xff0c;…

PyTorch训练深度卷积生成对抗网络DCGAN

文章目录 DCGAN介绍代码结果参考 DCGAN介绍 将CNN和GAN结合起来&#xff0c;把监督学习和无监督学习结合起来。具体解释可以参见 深度卷积对抗生成网络(DCGAN) DCGAN的生成器结构&#xff1a; 图片来源&#xff1a;https://arxiv.org/abs/1511.06434 代码 model.py impor…

Electron入门,项目启动。

electron 简单介绍&#xff1a; 实现&#xff1a;HTML/CSS/JS桌面程序&#xff0c;搭建跨平台桌面应用。 electron 官方文档&#xff1a; [https://electronjs.org/docs] 本文是基于以下2篇文章且自行实践过的&#xff0c;可行性真实有效。 文章1&#xff1a; https://www.cnbl…

基于Javaweb的摄影作品网站/摄影网站

摘 要 随着信息化时代的到来&#xff0c;系统管理都趋向于智能化、系统化&#xff0c;摄影作品网站也不例外&#xff0c;但目前国内的有些网站仍然都使用人工管理&#xff0c;浏览网站人数越来越多&#xff0c;同时信息量也越来越庞大&#xff0c;人工管理显然已无法应对时代的…

AMD fTPM RNG的BUG使得Linus Torvalds不满

导读因为在 Ryzen 系统上对内核造成了困扰&#xff0c;Linus Torvalds 最近在邮件列表中表达了对 AMD fTPM 硬件随机数生成器的不满&#xff0c;并提出了禁用该功能的建议。 因为在 Ryzen 系统上对内核造成了困扰&#xff0c;Linus Torvalds 最近在邮件列表中表达了对 AMD fTPM…