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.rhkb.cn/news/96177.html

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

相关文章

mac 可以进行单片机(stm32)的开发吗?

当涉及到在Mac上进行单片机开发时&#xff0c;是完全可行的。以下是为什么Mac适合单片机开发的解释&#xff1a;开发工具&#xff1a;针对STM32单片机&#xff0c;你可以使用多种开发工具。一个常用的选择是Segger Embedded Studio&#xff0c;它是一个功能强大的集成开发环境&…

ONNX版本YOLOV5-DeepSort (rknn版本已经Ready)

目录 1. 前言 2. 储备知识 3. 准备工作 4. 代码修改的地方 5.结果展示 1. 前言 之前一直在忙着写文档&#xff0c;之前一直做分类&#xff0c;检测和分割&#xff0c;现在看到跟踪算法&#xff0c;花了几天时间找代码调试&#xff0c;看了看&#xff0c;展示效果比单纯的检…

DevOps系列文章之 GitlabCICD自动化部署SpringBoot项目

一、概述 本文主要记录如何通过Gitlab CI/CD自动部署SpringBoot项目jar包。 二、前期准备 准备三台 CentOS7服务器&#xff0c;分别部署以下服务&#xff1a; 序号系统IP服务1CentOS7192.168.56.10Gitlab2CentOS7192.168.56.11Runner &#xff08;安装Docker&#xff09;3Cen…

Seata简介

1、简介 Seata是一个开源的分布式事务解决方案&#xff0c;用于解决分布式系统中的事务一致性问题。它提供了高性能和高可靠性的分布式事务支持&#xff0c;可以在微服务架构中保证数据的一致性和可靠性。 Seata的核心概念包括三个组件&#xff1a;事务协调器&#xff08;Tra…

如何正确下载tomcat???

亲爱的小伙伴&#xff0c;千万别再去找下网站下载啦&#xff0c;这样詪容易携带病毒。 我们去官方网址下载。 Apache Tomcat - Welcome! 最后下载解压即可。。。

【变形金刚02】注意机制以及BERT 和 GPT

一、说明 我已经解释了什么是注意力机制&#xff0c;以及与转换器相关的一些重要关键字和块&#xff0c;例如自我注意、查询、键和值以及多头注意力。在这一部分中&#xff0c;我将解释这些注意力块如何帮助创建转换器网络&#xff0c;注意、自我注意、多头注意、蒙面多头注意力…

无涯教程-Perl - undef函数

描述 此函数未定义EXPR的值。用于标量,列表,哈希,函数或类型范围。在带有诸如undef $hash {$key}之类的语句的哈希上使用&#xff1b;实际上将指定键的值设置为未定义的值。 如果要从哈希中删除元素,请使用delete函数。 语法 以下是此函数的简单语法- undef EXPRundef返回…

IronPDF for .NET Crack

IronPDF for .NET Crack ronPDF现在将等待HTML元素加载后再进行渲染。 IronPDF现在将等待字体加载后再进行渲染。 添加了在绘制文本时指定旋转的功能。 添加了在保存为PDFA时指定自定义颜色配置文件的功能。 IronPDF for.NET允许开发人员在C#、F#和VB.NET for.NET Core和.NET F…

QT实现天气预报

1. MainWindow类设计的成员变量和方法 public: MainWindow(QWidget* parent nullptr); ~MainWindow(); protected: 形成文本菜单来用来右键关闭窗口 void contextMenuEvent(QContextMenuEvent* event); 鼠标被点击之后此事件被调用 void mousePressEvent(QMouseEv…

软件测试项目实战,电商业务功能测试点汇总(全覆盖)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 支付功能怎么测试…

如何进行远程debug?

文章目录 前言一、使用步骤1.首先通过nohup在启动jar包的我们可以添加参数&#xff1a;2.具体参数的含义如下&#xff1a;3. 查询监听的端口&#xff1a; 前言 在工作中&#xff0c;排查问题我们经常需要进行debug&#xff0c;而远程debug能够方便的帮助我们排查线上的问题。 …

大数据平台运维实训室建设方案

一、概况 本实训室的主要目的是培养大数据平台运维项目的实践能力,以数据计算、分析、挖掘和可视化的案例训练为辅助。同时,实训室也承担相关考评员与讲师培训考试、学生认证培训考试、社会人员认证培训考试、大数据技能大赛训练、大数据专业课程改革等多项任务。 实训室旨在培…

Chrome

Chrome 简介下载 简介 Chrome 是由 Google 开发的一款流行的网络浏览器。它以其快速的性能、强大的功能和用户友好的界面而闻名&#xff0c;并且在全球范围内被广泛使用。Chrome 支持多种操作系统&#xff0c;包括 Windows、macOS、Linux 和移动平台。 Chrome官网: https://ww…

【Django】Task1安装python环境及运行项目

【Django】Task1安装python环境及运行项目 写在最前 8月份Datawhale组队学习&#xff0c;在这个群除我佬的时代&#xff0c;写一下blog记录学习过程。 参考资源&#xff1a; 学习项目github&#xff1a;https://github.com/Joe-2002/sweettalk-django4.2 队长博客&#xff1a…

【TypeScript】tsc -v 报错 —— 在此系统上禁止运行脚本

在 VS Code 终端中执行 tsc -v &#xff0c;报错 —— 在此系统上禁止运行脚本 然后 windows x &#xff0c;打开终端管理员&#xff0c;出现同样的问题 解决方法&#xff1a; 终端&#xff08;管理员&#xff09;执行以下命令&#xff1a; 出现 RemoteSigned 则代表更改成功…

广州华锐互动:奶牛难产原因及救治VR仿真实训系统

奶牛难产是一种常见的疾病&#xff0c;对奶牛的健康和生产造成很大的影响。为了解决这一问题&#xff0c;许多奶牛养殖场开始采用VR仿真技术来培训奶牛兽医&#xff0c;帮助学生更好地理解奶牛养殖的实际过程&#xff0c;提高他们的实践能力的教学方式。 VR技术开发公司广州华锐…

Java二分法查找

二分法&#xff1a;首先需要一个由小到大排序好的数组&#xff0c;先找到其中间值&#xff0c;然后进行比较如果比较中间值大的话则向前找。如果比要找的小&#xff0c;则向后找。 代码实现&#xff1a; //定义查询方法 public static int searchTarget(int[] nums, int targ…

centos安装elasticsearch7.9

安装es 下载elasticsearch安装包解压安装包,并修改配置文件解压进入目录修改配置文件 添加用户&#xff0c;并修改所有者切换用户&#xff0c;运行es如何迁移旧版本的数据 下载elasticsearch安装包 下载地址如下&#xff0c;版本号可以替换成自己想要的。 这里需要注意一点&am…

excel中定位条件,excel中有哪些数据类型、excel常见错误值、查找与替换

一、如何定位条件 操作步骤&#xff1a;开始 - 查找和选择 - 定位条件&#xff08;ctrl G 或 F5&#xff09; 注&#xff1a;如果F5不可用&#xff0c;可能是这个快捷键被占用了 案例&#xff1a;使用定位条件选择取余中空单元格&#xff0c;填入100&#xff0c;按组合键ct…

改进YOLO系列:2.添加ShuffleAttention注意力机制

添加ShuffleAttention注意力机制 1. ShuffleAttention注意力机制论文2. ShuffleAttention注意力机制原理3. ShuffleAttention注意力机制的配置3.1common.py配置3.2yolo.py配置3.3yaml文件配置1. ShuffleAttention注意力机制论文 论文题目:SA-NET: SHUFFLE ATTENTION …