前言
文章写在了 OpenSSL 密码学 专栏当中,是因为在使用 C++17 内存池时,使用了 OpenSSL 的加解密接口。
因为可以节省了工程配置,所有在 OpenSSLProject 项目中,直接创建了 MemoryPoolProjec 工程。并将 OpenSSL 的头文件和库文件添加到了工程中。
高效内存池的实现
为什么要实现高效内存池?
在服务器程序开发中,需要程序能长期稳定的运行,而 C++ 程序且不说内存泄漏问题,随着不断频繁的 new 和 delete,也会使堆空间的数据碎片化,导致new 和 delete 的效率下降,而影响你开发的程序效率。
而作为一个游戏服务器,要求的更高,无数的玩家不同行为,长期运行有可能几周甚至几个月,直至服务器维护。
由于程序要长时间运行、对速度要求高、对稳定性要求高。因此引入了高效内存池,不能让其程序肆意妄为的 new 和 delete,最终导致堆空间的数据碎片严重,而影响了服务器程序的效率,减少宕机风险。
如何解决频繁的 new 和 delete 问题
不让程序员去 new 和 delete ? 这太不切合实际了,那我们应该怎么做呢 ?
当然有办法,可以重载全局的 new 和 delete。
重载全局的 new 和 delete 的好处:
•监测内存创建销毁 ,统计和监控泄漏。•内存对齐的处理。
当然,我们要做的是实现自己的内存池,然后通过重载全局的 new 和 delete ,来反复使用内存池中的空间。
如何设计一个内存池
先看一下内存池需求:
•避免内存碎片•提高程序稳定性•减少申请内存时间。
看一下内存池设计图:

因此我们需要做的一个大小固定、提前申请、重复利用的内存池。
当我们 new 的时候,可以在内存池中直接找到我们已经申请好的空间进行使用。

代码的实现
内存块结构定义:
MemoryMgr.h 文件:
class MemoryBlock{public:size_t nRef = 0; // 引用计数MemoryAlloc* pAlloc = nullptr; // 内存块链表 分配器指针MemoryBlock* pNext = nullptr; // 下一个内存块指针bool bPool = false; // 是否使用内存池 如果是 false 会走默认的 malloc 和 free 去申请释放空间// 通过宏进行 补位,并没有实际意义,只是防止编译器优化,使 MemoryBlock x86 占用 16字节, x64 占用 32 字节#ifdef _WIN32//windows x86 or x68#ifdef _WIN64 //x64char cReserver[7] = { 0 };#else //x86char cReserver[3] = { 0 };#endif //_WIN64#else //unix#ifdef __x86_64__ //x64char cReserver[7] = { 0 };#elif __i386__ //x86char cReserver[3] = { 0 };#endif#endif //_WIN32};
内存分配器实现:
MemoryMgr.h 文件:
class MemoryAlloc{public:MemoryAlloc(){m_pBuf = 0;m_nInitBlock= 1024;}~MemoryAlloc(){_freeNotInPool();/*** free*/if (m_pBuf != 0){::free(m_pBuf);}}/*** alloc memory*/void* allocMemory(size_t nSize){if (m_pBuf == 0){// 程序在第一次 new 的时候会申请内存池,以后便都在内存池中查找空间_initMemory();}MemoryBlock* pReturn = 0;/****/if (m_pHeader == 0){pReturn = (MemoryBlock*)(::malloc(nSize + sizeof(MemoryBlock)));pReturn->bPool = false;pReturn->nRef = 1;pReturn->pAlloc = this;pReturn->pNext = 0;}else{/*** get one node from free list*/pReturn = m_pHeader;m_pHeader = m_pHeader->pNext;pReturn->pAlloc = this;pReturn->bPool = true;assert(pReturn->nRef == 0);pReturn->nRef = 1;}return ((char*)pReturn) + sizeof(MemoryBlock);}/*** free memory function*/void freeMemory(void* pMem){char* pCh = (char*)pMem;MemoryBlock*pMap = (MemoryBlock*)((char*)(pCh - sizeof(MemoryBlock)));/*** ref*/if (--pMap->nRef != 0){return;}if (!pMap->bPool){::free(pMap);return;}pMap->pNext = m_pHeader;m_pHeader = pMap;}/*** init memory pool*/void _initMemory(){assert(m_pBuf == 0);if (m_pBuf != 0 ){return;}/*** 计算需要申请的内存大小,其中包含内存头数据大小*/size_t nBuf = m_nSize * m_nInitBlock;/*** 申请内存*/m_pBuf = (char*)::malloc(nBuf);/*** 链表头和尾部指向同一位置*/m_pHeader = (MemoryBlock*)m_pBuf;m_pHeader->nRef = 0;m_pHeader->pAlloc = this;m_pHeader->pNext = 0;m_pHeader->bPool = true;MemoryBlock* pTemp = m_pHeader;for (size_t i = 1 ;i < m_nInitBlock ; ++ i){char* pBlock = (m_pBuf + m_nSize * i);MemoryBlock*pMap = (MemoryBlock*)pBlock;pMap->nRef = 0;pMap->bPool = true;pMap->pAlloc = this;pMap->pNext = 0;pTemp->pNext = pMap;pTemp = pMap;}}/*** 释放所有数据* 该函数目的是将所有的不在pool中的内存释放掉*/void _freeNotInPool(){/*** 将不在内存池中的临时内存删除掉*/while(m_pHeader != 0){MemoryBlock* pHeader = m_pHeader;m_pHeader = m_pHeader->pNext;if (!pHeader->bPool){::free(pHeader);}}m_pHeader = 0;}/*** 得到当前池中可用的对象数*/size_t _getFreeBlock(){MemoryBlock* pTemp = m_pHeader;size_t nSize = 0;while(pTemp != 0){++nSize;pTemp = pTemp->pNext;}return nSize;}protected:char* m_pBuf;MemoryBlock* m_pHeader;size_t m_nInitBlock;size_t m_nSize;};// 通过模版定义,目的可以快速找到 申请不同大小内存空间时,快速找到对应的内存块template<size_t nBlock,size_t nInitSize>class MemoryAlloctor:public MemoryAlloc{public:MemoryAlloctor(){m_nInitBlock = nInitSize;m_nSize = nBlock / sizeof(void*) * sizeof(void*) + (nBlock% sizeof(void*) ? sizeof(void*) : 0 );m_nSize = m_nSize + sizeof(MemoryBlock);}};
内存管理类:
MemoryMgr.h 文件:
class MemoryMgr{public:MemoryMgr(void){init(0, 64, &m_mem64);init(64,128, &m_mem128);init(128, 256, &m_mem256);init(256, 512, &m_mem512);init(512, 1024, &m_mem1024);init(1024, 2048, &m_mem2048);init(2048, 4096, &m_mem4096);}~MemoryMgr(void){}static MemoryMgr& instance(){static MemoryMgr sInstance;return sInstance;}/*** 申请内存*/void* alloc(size_t nSize){if ( nSize < sizeof(m_arAlloc)/sizeof(MemoryAlloc*)){return m_arAlloc[nSize]->allocMemory(nSize);}else{char* pMem = (char*)::malloc(nSize + sizeof(MemoryBlock));MemoryBlock*pBlock = (MemoryBlock*)pMem;pBlock->bPool = false;pBlock->nRef = 1;pBlock->pAlloc = 0;pBlock->pNext = 0;return pMem + sizeof(MemoryBlock);}}/*** 释放内存*/void free(void* pMem){char* pCh = (char*)pMem;MemoryBlock*pMap = (MemoryBlock*)(char*)(pCh - sizeof(MemoryBlock));if (pMap->bPool){pMap->pAlloc->freeMemory(pMem);}else if(--pMap->nRef == 0){::free(pMap);}}/*** 添加引用计数*/void addRef(void* pMem){MemoryBlock*pMap = (MemoryBlock*)((char*)pMem - sizeof(MemoryBlock));++pMap->nRef;}protected:void init(size_t nBegin,size_t nEnd,MemoryAlloc* pAlloc){for (size_t i = nBegin ;i < nEnd ; ++ i){m_arAlloc[i] = pAlloc;}}protected:MemoryAlloctor<64,10240> m_mem64;MemoryAlloctor<128,10240> m_mem128;MemoryAlloctor<256,10240> m_mem256;MemoryAlloctor<512,10240> m_mem512;MemoryAlloctor<1024,10240> m_mem1024;MemoryAlloctor<2048,10240> m_mem2048;MemoryAlloctor<4096,10240> m_mem4096;MemoryAlloc* m_arAlloc[4096];};
重载全局的 new 和 delete:
Alloctor.h
#pragma oncevoid* operator new(size_t nSize);void operator delete(void* pMem);void* operator new[](size_t nSize);void operator delete[](void* pMem);void* mem_alloc(size_t nSize);void mem_free(void* pMem);
Alloctor.cpp
#include "../include/Alloctor.h"#include "../include/MemoryMgr.h"void* operator new(size_t nSize){return MemoryMgr::instance().alloc(nSize);}void operator delete(void* pMem){MemoryMgr::instance().free(pMem);}void* operator new[](size_t nSize){return MemoryMgr::instance().alloc(nSize);}void operator delete[](void* pMem){MemoryMgr::instance().free(pMem);}void* mem_alloc(size_t nSize){return MemoryMgr::instance().alloc(nSize);}void mem_free(void* pMem){MemoryMgr::instance().free(pMem);}
测试
main.cpp
#include "../include/Alloctor.h"#include "../include/MemoryMgr.h"#include <string>#define OTA_STRING_MAX_LEN 256///软件信息typedef struct{char productID[OTA_STRING_MAX_LEN]; // 产品IDchar softVer[OTA_STRING_MAX_LEN]; // 软件版本char url[OTA_STRING_MAX_LEN]; // 下载地址int iTotalSize; // 总sizechar createDateTime[OTA_STRING_MAX_LEN]; // 发布时间char detileInfo[OTA_STRING_MAX_LEN]; // 详细描述char englishDetailInfo[OTA_STRING_MAX_LEN]; // 英文详细描述char sha256[OTA_STRING_MAX_LEN];}OTA_SoftPackeInfo;int main(int _Argc, char* argv[]){char* data = new char[112];delete[] data;OTA_SoftPackeInfo *soft_packe_info = new OTA_SoftPackeInfo();delete soft_packe_info;return 0;}
C++17 内存池的应用
C++17 内存池
在C++17中已经有了内存池,看一下结构图:

•memory_resource:这是一个虚基类(接口),表示对内存资源的管理。•pool_options:内存池配置。•synchronized_pool_resource:memory_resource的子类,具体实现线程安全的内存管理,显然在分配和释放时加有锁。•unsynchronized_pool_resource:memory_resource的子类,具体实现非线程安全的内存管理,在单线程时应用时效率高。
使用内存池文件加解密
由于工程需要使用 C++17 ,因此先将工程设置为使用 C++17 :

设计类结构:

•XCrypt 类:加解密类•XIOStream 类:线程基类,责任链模式、多线程数据传递、内存池注入和空间管理•XReadTask 类:文件读取类,继承 XIOStream 读取文件,发送给下一个责任链,需要告知下一个责任链任务结束•XCryptTask 类:加解密处理线程,继承 XIOStream 接收 XReadTask 传递数据•XWriteTask 类:写入文件现场,继承 IOStream 接收 XCryptTask 发送的加解密数据•XFileCrypt 类:处理文件加解密任务,对整体流程的封装
XData.h
/*************************************************************************************************************** vic.MINg 2021-09-13* 数据类**************************************************************************************************************/#pragma once#include <memory>#include <memory_resource>class XData{public:~XData();void* New(long long mem_size);void Delete();// 设置实际数据字节数void set_size(long long s) { data_size_ = s; }long long size() { return data_size_; }void* data() { return data_; }bool end() { return is_end_; }void set_end(bool e) { is_end_ = e; }// 创建 XData 智能指针对象static std::shared_ptr<XData> Make(std::shared_ptr<std::pmr::memory_resource> pool);private:XData() {} // 私有 不能自己创建bool is_end_ = false;void* data_ = nullptr;long long data_size_ = 0; // 数据字节数long long memory_size_ = 0; // 占用空间字节数std::shared_ptr<std::pmr::memory_resource> memory_pool_;};
XData.cpp
#include "../include/XData.h"#include <iostream>std::shared_ptr<XData> XData::Make(std::shared_ptr<std::pmr::memory_resource> pool){std::shared_ptr<XData> ptr(new XData);ptr->memory_pool_ = pool;return ptr;}void* XData::New(long long mem_size){if (mem_size <= 0){std::cerr << "XData::New failed! mem_size<=0" << std::endl;return nullptr;}if (!memory_pool_)return nullptr;data_ = memory_pool_->allocate(mem_size);this->memory_size_ = mem_size;this->data_size_ = mem_size;//cout << "+" << flush;return data_;}void XData::Delete(){if (!data_ || !memory_pool_) return;memory_pool_->deallocate(data_, memory_size_);data_ = nullptr;memory_size_ = 0;data_size_ = 0;}XData::~XData(){Delete();}
XCrypt.h
/*************************************************************************************************************** vic.MINg 2021-09-13* 加解密类 DES加密算法**************************************************************************************************************/#pragma once#include <string>#include <openssl/des.h>class XCrypt{public:// 初始化秘钥 秘钥最多8位 多余丢弃不足补0bool Init(std::string password);// 加密数据,结尾填充补充的大小,加密数据大小如果不是 8、16的倍数int Encrypt(const char* in_data, int insize, char* out_data, bool is_end = false);// 解密数据,结尾去掉填充大小int Decrypt(const char* in_data, int insize, char* out_data, bool is_end = false);//获取需要填充的数据字节数int GetPadding(int datasize);private:// 存储秘钥DES_key_schedule key_sch_;};
XCrypt.cpp
#include "../include/XCrypt.h"#include <iostream>bool XCrypt::Init(std::string password){DES_cblock key = { 0 }; // 不足补0int key_size = password.size();if (key_size > sizeof(key)) // 多余丢弃{key_size = sizeof(key);}memcpy(key.bytes, password.c_str(), key_size);DES_set_key(&key, &key_sch_);return true;}int XCrypt::GetPadding(int datasize){const int block_size = sizeof(DES_cblock);int padding = block_size - datasize % block_size;if (padding == 0) padding = block_size;return padding;}int XCrypt::Encrypt(const char* in_data, int insize, char* out_data, bool is_end){if (!in_data || insize <= 0 || !out_data) return 0;int write_size = 0;DES_cblock in = { 0 }; // 输入数据DES_cblock out = { 0 }; // 输出const int block_size = sizeof(DES_cblock);int data_size = 0;int padding = block_size - insize % block_size;for (int i = 0; i < insize; i += block_size){if (insize - i < block_size){data_size = insize - i;}else{data_size = block_size;}// 复制数据源memcpy(in.bytes, in_data + write_size, data_size);//填充 补充的数据大小if (is_end && i + block_size >= insize) // 处理最后一块数据{if (padding == block_size){DES_ecb_encrypt(&in, &out, &key_sch_, DES_ENCRYPT);memcpy(out_data + write_size, &out.bytes, block_size);write_size += block_size;memset(in.bytes, padding, sizeof(in)); // 填充8}else{memset(in.bytes + insize % block_size, padding, padding);}}// 加密数据DES_ecb_encrypt(&in, &out, &key_sch_, DES_ENCRYPT);memcpy(out_data + write_size, &out.bytes, block_size);write_size += block_size;}return write_size;}int XCrypt::Decrypt(const char* in_data, int insize, char* out_data, bool is_end){if (!in_data || insize <= 0 || !out_data) return 0;int write_size = 0;DES_cblock in = { 0 }; // 输入数据DES_cblock out = { 0 }; // 输出const int block_size = sizeof(DES_cblock);int data_size = 0;for (int i = 0; i < insize; i += block_size){memcpy(in.bytes, in_data + write_size, block_size);// 解密DES_ecb_encrypt(&in, &out, &key_sch_, DES_DECRYPT);data_size = block_size;// 处理结尾填充if (is_end && insize - i <= block_size){data_size = block_size - out.bytes[7];if (data_size == 0){break;}else if (data_size < 0){std::cerr << "Decrypt failed!padding size error!" << std::endl;break;}}memcpy(out_data + write_size, &out.bytes, data_size);write_size += data_size;}return write_size;}
XIOStream.h
/*************************************************************************************************************** vic.MINg 2021-09-13* 线程基类,责任链模式、多线程数据传递、内存池注入和空间管理**************************************************************************************************************/#pragma once#include <thread>#include <mutex>#include <list>#include <memory_resource>class XData;class XIOStream{public:// 线程启动void Start();// 等待线程退出void Wait();// 线程退出,需要用 Wait 等待void Stop() { is_exit_ = true; }void set_mem_pool(std::shared_ptr<std::pmr::memory_resource> mp) { memory_pool_ = mp; }// 设置责任链下一个节点void set_next(std::shared_ptr<XIOStream> next) { task_next_ = next; }// 给对象传递数据,线程安全void PushBack(std::shared_ptr<XData> data);std::shared_ptr<XData> PopFront();protected://线程入口virtual void DoIt() {}bool is_exit_ = false;long long data_byte_ = 0; // 所有要处理数据的字节数std::shared_ptr<std::pmr::memory_resource> memory_pool_; // 线程池std::shared_ptr<XIOStream> task_next_; // 责任链代码private:std::thread io_thread_;std::mutex io_mutex_;std::list<std::shared_ptr<XData>> io_datas_;};
XIOStream.cpp
#include "../include/XIOStream.h"#include "../include/XData.h"void XIOStream::Start(){io_thread_ = std::thread(&XIOStream::DoIt, this);}void XIOStream::Wait(){if (io_thread_.joinable())io_thread_.join();}std::shared_ptr<XData> XIOStream::PopFront(){std::unique_lock<std::mutex> lock(io_mutex_);if (io_datas_.empty())return nullptr;auto re = io_datas_.front();io_datas_.pop_front();return re;}void XIOStream::PushBack(std::shared_ptr<XData> data){std::unique_lock<std::mutex> lock(io_mutex_);io_datas_.push_back(data);}
XReadTask.h
/*************************************************************************************************************** vic.MINg 2021-09-13* 文件读取类**************************************************************************************************************/#pragma once#include "XIOStream.h"#include <string>#include <fstream>class XReadTask :public XIOStream{public:// 初始化读取线程,获取文件大小bool Init(std::string filename);private:void DoIt();std::ifstream read_stream_; //读取文件};
XReadTask.cpp
#include "../include/XReadTask.h"#include "../include/XData.h"#include <iostream>bool XReadTask::Init(std::string filename){if (filename.empty())return false;read_stream_.open(filename, std::ios::binary); //二进制打开if (!read_stream_){std::cerr << "open file " << filename << " failed!" << std::endl;return false;}std::cout << filename << " open success!" << std::endl;read_stream_.seekg(0, std::ios::end);data_byte_ = read_stream_.tellg();read_stream_.seekg(0, std::ios::beg);std::cout << " file size " << data_byte_ << std::endl;return true;}void XReadTask::DoIt(){std::cout << "begin thread XReadTask::Main" << std::endl;while (!is_exit_){if (read_stream_.eof())break;// 创建内存池空间管理对象auto data = XData::Make(this->memory_pool_);int data_size = 1024;// 申请空间void* buf = data->New(data_size);//读取文件read_stream_.read((char*)buf, data_size);if (read_stream_.gcount() <= 0)break;data->set_size(read_stream_.gcount());if (read_stream_.eof()){data->set_end(true);}if (task_next_){task_next_->PushBack(data);}}read_stream_.close();std::cout << "end thread XReadTask::Main" << std::endl;}
XCryptTask.h
/*************************************************************************************************************** vic.MINg 2021-09-13* 加解密处理线程类**************************************************************************************************************/#pragma once#include "XIOStream.h"class XCrypt;typedef enum {Crypt_Null, // 空Crypt_Encrypt, // 加密Crypt_Decrypt // 解密}CryptType;class XCryptTask :public XIOStream{public://初始化加解密秘钥void Init(std::string passwd);void set_crypt_type(CryptType type) { crypt_type_ = type; }private:void DoIt();std::shared_ptr<XCrypt> crpyt_;CryptType crypt_type_ = Crypt_Null;};
XCryptTask.cpp
#include "../include/XCryptTask.h"#include "../include/XCrypt.h"#include "../include/XData.h"#include <iostream>#include <thread>#include <chrono>void XCryptTask::Init(std::string passwd){crpyt_ = std::make_shared<XCrypt>();crpyt_->Init(passwd);}void XCryptTask::DoIt(){std::cout << "begin XCryptTask::Main()" << std::endl;while (!is_exit_){auto data = PopFront();if (!data){std::this_thread::sleep_for(std::chrono::milliseconds(10));continue;}auto out = XData::Make(memory_pool_);int outsize = data->size() + crpyt_->GetPadding(data->size());out->New(outsize);int data_size = 0;bool is_end = data->end();switch (crypt_type_){case Crypt_Null:break;case Crypt_Encrypt:data_size = crpyt_->Encrypt((char*)data->data(), data->size(), (char*)out->data(), is_end);break;case Crypt_Decrypt:data_size = crpyt_->Decrypt((char*)data->data(), data->size(), (char*)out->data(), is_end);break;default:break;}out->set_size(data_size);std::cout << "{" << out->size() << "}" << std::flush;out->set_end(data->end());if (task_next_){task_next_->PushBack(out);}if (data->end()){break;}}std::cout << "end XCryptTask::Main()" << std::endl;}
XWriteTask.h
/*************************************************************************************************************** vic.MINg 2021-09-13* 文件写入类**************************************************************************************************************/#pragma once#include "XIOStream.h"#include <fstream>class XWriteTask :public XIOStream{public:bool Init(std::string filename);private:void DoIt();std::ofstream write_stream_;};
XWriteTask.cpp
#include "../include/XWriteTask.h"#include "../include/XData.h"#include <iostream>bool XWriteTask::Init(std::string filename){write_stream_.open(filename, std::ios::binary);if (!write_stream_){std::cerr << "open file " << filename.c_str() << " failed!" << std::endl;return false;}std::cout << filename.c_str() << " open success!" << std::endl;return true;}void XWriteTask::DoIt(){while (!is_exit_){auto data = PopFront();if (!data){std::this_thread::sleep_for(std::chrono::milliseconds(10));continue;}write_stream_.write((char*)data->data(), data->size());if (data->end())break;}write_stream_.close();}
XFileCrypt.h
/*************************************************************************************************************** vic.MINg 2021-09-13* 处理文件加解密任务,对整体流程的封装**************************************************************************************************************/#pragma once#include <string>#include <memory>#include "XCryptTask.h"class XReadTask;class XWriteTask;class XFileCrypt{public:bool Start(std::string infile, std::string outfile, std::string passwd, CryptType type = Crypt_Encrypt);void Wait();private:std::shared_ptr<XReadTask> read_task_;std::shared_ptr<XCryptTask> crypt_task_;std::shared_ptr<XWriteTask> write_task_;};
XFileCrypt.cpp
#include "../include/XFileCrypt.h"#include "../include/XReadTask.h"#include "../include/XWriteTask.h"#include <iostream>bool XFileCrypt::Start(std::string infile, std::string outfile, std::string passwd, CryptType type){//创建一个内存池std::shared_ptr<std::pmr::memory_resource> mp(new std::pmr::synchronized_pool_resource());read_task_ = std::make_shared<XReadTask>();read_task_->Init(infile);read_task_->set_mem_pool(mp);crypt_task_ = std::make_shared<XCryptTask>();crypt_task_->set_mem_pool(mp);crypt_task_->set_crypt_type(type);crypt_task_->Init(passwd);read_task_->set_next(crypt_task_);write_task_ = std::make_shared<XWriteTask>();write_task_->set_mem_pool(mp);write_task_->Init(outfile);crypt_task_->set_next(write_task_);read_task_->Start();crypt_task_->Start();write_task_->Start();return true;}void XFileCrypt::Wait(){if (read_task_)read_task_->Wait();if (crypt_task_)crypt_task_->Wait();if (write_task_)write_task_->Wait();}
测试
main.cpp
#include "../include/XFileCrypt.h"#include <string>#include <memory>#include "../include/DirectoryManager.h"int main(int _Argc, char* argv[]){std::string passwd = "vic.MINg";{std::string strText_source, strText_crypt, strText_target;#ifdef _WIN32strText_source = DirectoryManager::GetExeDirectory() + "test_source.txt";strText_crypt = DirectoryManager::GetExeDirectory() + "test_crypt.txt";strText_target = DirectoryManager::GetExeDirectory() + "test_target.txt";#elsestrText_source = "test_source.txt";strText_crypt = "test_crypt.txt";strText_target = "test_target.txt";#endifauto xfc = std::make_shared<XFileCrypt>();xfc->Start(strText_source, strText_crypt, passwd, CryptType::Crypt_Encrypt);xfc->Wait();auto xfd = std::make_shared<XFileCrypt>();xfd->Start(strText_crypt, strText_target, passwd, CryptType::Crypt_Decrypt);xfd->Wait();}system("pause");return 0;}




