1.线程处理
1.1 界面类设计
DWORD WINAPI GetExportFileInfoThreadFunc(LPVOID Iparam);
class CExportImageDlg : public CDialog
{
public:
//! 导出图像数据线程函数
friend DWORD WINAPI GetExportFileInfoThreadFunc(LPVOID Iparam);
//! 获取导出图像信息
void GetExportFileInfo()
{}
private:
//!导出线程句柄
HANDLE m_hExportImageInfo;
}
1.2 创建线程
//创建控制线程,获取导出图像信息
m_hExportImageInfo = ::CreateThread(NULL, 0, GetExportFileInfoThreadFunc, (LPVOID)this, 0, NULL);
DWORD WINAPI GetExportFileInfoThreadFunc(LPVOID Iparam)
{
CExportImageDlg *pExportImageDlg = (CExportImageDlg *)Iparam;
ASSERT(pExportImageDlg);
if (pExportImageDlg)
{
pExportImageDlg->GetExportFileInfo();
}
return 0;
}
1.3 保证线程已经退出
if (m_hExportImageInfo != NULL)
{
const int nMaxThreadExitTimes = 5;
int nTimes = 0;
while ((WaitForSingleObject(m_hExportImageInfo, 200) == WAIT_TIMEOUT) && (nTimes < nMaxThreadExitTimes))
{
nTimes++;
}
if (nTimes >= nMaxThreadExitTimes)
{
TerminateThread(m_hExportImageInfo, 0);
}
CloseHandle(m_hExportImageInfo);
m_hExportImageInfo = NULL;
}
VOID ExitThread(UINTfuExitCode ); 函数终止自己
1.4 线程同步
多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,无法控制数据,变成随机变量。为解决这个问题,就需要引入互斥变量,让每个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和LeaveCriticalSection函数。
CRITICAL_SECTION m_crit;//一个临界区
::InitializeCriticalSection(&m_crit);//初始化临界区
::EnterCriticalSection(&m_crit);
//此处为线程函数内需要保护的数据
::LeaveCriticalSection(&m_crit);
1.5 设置线程优先级
线程优先级 =进程类基本优先级+线程相对优先级
BOOL SetThreadPriority(HANDLE hThread, int nPriority);
1.6 判断线程是否结束
获取线程退出码:
BOOL WINAPIGetExitCodeThread( HANDLE hThread, LPDWORD lpExitCode);
如果线程尚未结束,lpExitCode带回来的将是STILL_ALIVE。
1.7 挂起与恢复线程
挂起线程:DWORD SuspendThread(HANDLEhThread);
恢复线程:DWORD ResumeThread(HANDLEhThread);
1.8 多线程操作list,使用Mutex
#ifndef F_Test_20171026_18_27_JHASKDFJHASF_H_
#define F_Test_20171026_18_27_JHASKDFJHASF_H_
#include <stdio.h>
#include "Windows.h"
#include "afxmt.h"
/*
1. 测试线程同步,使用互斥锁
2. 多线程操作list,容易引起奔溃
3. MFC需要使用CMutex同步
*/
typedef struct TestData
{
int nCom;
list<CString> strDataList;
}sTestData;
list<sTestData> g_StrList;
CMutex g_MutexThread;
DWORD WINAPI ThreadPushDataToListFunc(LPVOID pParam);
DWORD WINAPI ThreadPrintDataFunc(LPVOID pParam);
void TestThreadMutex()
{
HANDLE hThreadPrint = CreateThread(NULL, 0, ThreadPrintDataFunc, NULL, 0, 0);
CloseHandle(hThreadPrint);
hThreadPrint = NULL;
HANDLE hThreadPush = CreateThread(NULL, 0, ThreadPushDataToListFunc, NULL, 0, 0);
CloseHandle(hThreadPush);
hThreadPush = NULL;
}
DWORD WINAPI ThreadPushDataToListFunc(LPVOID pParam)
{
sTestData sTestDataTemp;
for(int i=0; i<100000; i++)
{
sTestDataTemp.nCom = i;
sTestDataTemp.strDataList.clear();
sTestDataTemp.strDataList.push_back("aaaa");
sTestDataTemp.strDataList.push_back("bbbb");
sTestDataTemp.strDataList.push_back("cccc");
g_MutexThread.Lock();
g_StrList.push_back(sTestDataTemp);
g_MutexThread.Unlock();
TRACE("\n-----SendData----%d\n", i);
Sleep(2000);
}
return 0;
}
DWORD WINAPI ThreadPrintDataFunc(LPVOID pParam)
{
list<CString>::iterator itStr;
list<sTestData>::iterator itTest;
while(1)
{
g_MutexThread.Lock();
if(g_StrList.size() > 0)
{
for(itTest=g_StrList.begin(); itTest!=g_StrList.end(); itTest++)
{
TRACE("\n-----AcceptData----%d =>", itTest->nCom );
for (itStr=itTest->strDataList.begin(); itStr!=itTest->strDataList.end(); itStr++)
{
CString strTemp = *itStr;
TRACE("%s ", strTemp);
}
itTest->strDataList.clear();
Sleep(10);
}
g_StrList.clear();
}
g_MutexThread.Unlock();
TRACE("\n------------------------------------------------------------------\n");
Sleep(2000);
}
return 0;
}
#endif//F_Test_20171026_18_27_JHASKDFJHASF_H_
2.Gdi+ 绘图,绘字(MFC环境)
2.1 下载库
从网上下载相关Gdi+的库文件包,将文件拷贝到工程目录下。
下载地址:点击打开链接
2.2加载库
在文件 “StdAfx.h” 添加以下代码
#include "GDIPlus/Includes/GdiPlus.h" using namespace Gdiplus; #pragma comment(lib, "GdiPlus.lib")
2.3 启动Gdi+
使用前先启动,在使用之前添加以下代码
ULONG_PTR m_gdiplusToken; GdiplusStartupInput m_gdiplusStartupInput; GdiplusStartup(&m_gdiplusToken, &m_gdiplusStartupInput, NULL);
2.4 退出Gdi+
不使用时,记得关闭相关操作
GdiplusShutdown(m_gdiplusToken);
2.5 绘图
//绘图 void C***View::DrawBmpPicture(CDC *pDC, int nRectLeft, int nRectTop, CString strBmpPath, int nPictureWidth, int nPictureHeight) { HDC hdc = pDC->GetSafeHdc(); //gdi+ 处理对象 Graphics mygraphics(hdc); //CString转换为WCHAR int nLength = strlen(strBmpPath)+1; const char* tempStr = strBmpPath; int newLength = MultiByteToWideChar(CP_ACP, 0, tempStr, nLength, NULL, 0); WCHAR chPicturePath[128] = {0}; MultiByteToWideChar(CP_ACP, 0, strBmpPath, nLength, chPicturePath, newLength); //显示工位图片 Image image(chPicturePath, FALSE); Image* pThumbnail = image.GetThumbnailImage(nPictureWidth, nPictureHeight, NULL, NULL);//缩略图片 mygraphics.DrawImage(pThumbnail, nRectLeft, nRectTop, pThumbnail->GetWidth(), pThumbnail->GetHeight()); if (pThumbnail) { delete pThumbnail; } return ; }
2.6 绘字
//绘字 void C***View::DrawText(CDC *pDC, int nTextX, int nTextY, CString strText, DOUBLE fTextFont, Color colorText) { HDC hdc = pDC->GetSafeHdc(); //gdi+处理对象 Graphics mygraphics(hdc); //CString转换为WCHAR strText.Format(strText); const char* tempStr = strText; int nLength = strlen(tempStr)+1; int newLength = MultiByteToWideChar(CP_ACP, 0, tempStr, nLength, NULL, 0); WCHAR chStitionID[60] = {0}; MultiByteToWideChar(CP_ACP, 0, strText, nLength, chStitionID, newLength); //显示文字 SolidBrush brush(colorText); FontFamily fontFamily(L"Latha"); Font font(&fontFamily, (float)fTextFont, FontStyleRegular, UnitInch); PointF pointF((float)nTextX, (float)nTextY); //输出坐标 mygraphics.DrawString(chStitionID, -1, &font, pointF, &brush); return ; }
3.VC程序实现重启
void CTestDlg::OnBnClickedBtRestart() { ::PostMessage(AfxGetMainWnd()->m_hWnd, WM_SYSCOMMAND, SC_CLOSE, NULL); //获取exe程序当前路径 extern CDxRayAPITestApp theApp;//当前APP名称 TCHAR szAppName[MAX_PATH]; :: GetModuleFileName(theApp.m_hInstance, szAppName, MAX_PATH); CString strAppFullName; strAppFullName.Format(_T("%s"), szAppName); //重启程序 STARTUPINFO StartInfo; PROCESS_INFORMATION procStruct; memset(&StartInfo, 0, sizeof(STARTUPINFO)); StartInfo.cb = sizeof(STARTUPINFO); ::CreateProcess( (LPCTSTR)strAppFullName, NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &StartInfo, &procStruct); }
4.VC编程,获取上一次的错误
MSDN中通常会建议用GetLastError()来获取上一次调用系统API函数错误原因。 但是,GetLastError()返回的只是一个错误值(双字节数值(DWORD),具体请参照返回值列表:点击打开链接),没有说明具体的原因。因此可以用FormatMessage函数,获取产生异常的文字信息。需包含头文件:#include <windows.h>实例如下:#include <windows.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <assert.h> using namespace std; #define MAX_PATH 260 #define BUF_SIZE 5 #define WRITE_FILE_SIZE 24 void TestGetLastErrorFun(char **pError); int main(void) { char *pszBufW = new char(WRITE_FILE_SIZE); memset(pszBufW, 0, WRITE_FILE_SIZE); char szFilePath[MAX_PATH] = {0}; GetCurrentDirectory(MAX_PATH, szFilePath); strncat(szFilePath, "\\TestWrite_2.txt", MAX_PATH); FILE *pFile = fopen(szFilePath, "r"); //打开文件,读数据 TestGetLastErrorFun(&pszBufW); //此处调用,通过输出参数返回错误原因, 比如没找到文件,内存不足等 cout <<endl <<"LastError: " <<pszBufW <<endl; //输出错误原因 if (!pFile) { assert(pFile); return -1; } if(pszBufW) { delete pszBufW; pszBufW = NULL; } if(pFile) { fclose(pFile); pFile = NULL; } system("pause"); return 0; } void TestGetLastErrorFun(char **pError) { FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &(*pError), 0, NULL); }
5.RAII
将资源(如FILE*,HANDLE等)抽象为类,用局部对象来表示资源,把管理资源的任务转化为管理局部对象的任务。这就是RAII惯用法的真谛。
具体可参考:点击打开链接
用法1:需要用时,创建 新对象 RaiiFile
文件:RAII.h
文件:RAII.cpp#ifndef TEST_RAII_2017_3_18_SKDJFGH_H_ #define TEST_RAII_2017_3_18_SKDJFGH_H_ #include "afx.h" #define TEST_FILE_R "E:\\TestR.txt" #define TEST_FILE_W "E:\\TestW.txt" #define MAX_PATH 260 class RaiiFile { public: RaiiFile(const char *pFilePath, const char *openType) { try { m_pFile = fopen(pFilePath, openType); if (!m_pFile) { throw "Error: open file fail"; } } catch (char *pError) { printf(pError); } } ~RaiiFile() { if(m_pFile) { fclose(m_pFile); m_pFile = NULL; } } FILE *GetFile() { return m_pFile; } private: //禁止拷贝操作 RaiiFile(const RaiiFile &); const RaiiFile operator=(const RaiiFile &); public: FILE *m_pFile; }; //! 测试入口 void TestFile(); #endif
#include "RAII.h" void TestFile() { char szPathFileR[MAX_PATH] = {0}; strcat(szPathFileR, TEST_FILE_R); char szPathFileW[MAX_PATH] = {0}; strcat(szPathFileW, TEST_FILE_W); RaiiFile FileR(szPathFileR, "r");//如果中途返回,也会调用析构函数关闭文件 RaiiFile FileW(szPathFileW, "w"); //Copy File fseek(FileR.GetFile(), 0, SEEK_SET); int chTest; while(true) { chTest = fgetc(FileR.GetFile()); if (!feof(FileR.GetFile())) { fputc(chTest, FileW.GetFile()); } else { break; } } //此处即使返回, 也会调用 RaiiFile 的析构函数,关闭文件 }
用法2(简单,推荐):需要用时,无需创建 新对象,只需要将对象初始化到RAII, 即可直接使用 FILE*
文件:RAII_2.h
#ifndef TEST_RAII_2_2017_3_18_SKDJFGHDSDFG_H_ #define TEST_RAII_2_2017_3_18_SKDJFGHDSDFG_H_ #include "afx.h" #define TEST_FILE_R "E:\\TestR.txt" #define TEST_FILE_W "E:\\TestW.txt" #define MAX_PATH 260 class RaiiFile { public: RaiiFile(FILE *pFile) { if (pFile) { m_pFile = pFile; } else { printf("Error:open file fail"); } } ~RaiiFile() { if(m_pFile) { fclose(m_pFile); m_pFile = NULL; } } private: //禁止拷贝操作 RaiiFile(const RaiiFile &); const RaiiFile operator=(const RaiiFile &); public: FILE *m_pFile; }; //! 测试入口 void TestFile(); #endif //TEST_RAII_2_2017_3_18_SKDJFGHDSDFG_H_
文件: RAII_2.cpp
#include "RAII_2.h" void TestFile() { char szPathFileR[MAX_PATH] = {0}; strcat(szPathFileR, TEST_FILE_R); char szPathFileW[MAX_PATH] = {0}; strcat(szPathFileW, TEST_FILE_W); FILE* pFileR = fopen(szPathFileR, "r"); if (!pFileR) { printf("Error:open file fail"); return; } RaiiFile raiiFileR(pFileR);//如果中途返回,也会调用 raiiFileR析构函数 关闭文件 FILE* pFileW = fopen(szPathFileW, "w"); RaiiFile raiiFileW(pFileW);//如果中途返回,也会调用 raiiFileW析构函数 关闭文件 //Copy File fseek(pFileR, 0, SEEK_SET); int chTest; while(true) { chTest = fgetc(pFileR); if (!feof(pFileR)) { fputc(chTest, pFileW); } else { break; } } if(pFileR) { fclose(pFileR); pFileR = NULL; } if(pFileW) { fclose(pFileW); pFileW = NULL; } }
书写自己的RAII控制资源
#ifndef MY_RAII_2017_3_18_SKDJFGHDSDFG_H_ #define MY_RAII_2017_3_18_SKDJFGHDSDFG_H_ class RAIIBase { public: RAIIBase() {} ~RAIIBase() {} RAIIBase(const RAIIBase&); RAIIBase operator=(const RAIIBase&); }; template<class T> class MyRAIIResource: private RAIIBase //私有继承 禁用Base的所有继承操作 { public: explicit MyRAIIResource(T* ptMyResouce) :m_ptMyRAIIResource(ptMyResouce) {} ~MyRAIIResource() { delete m_ptMyRAIIResource; m_ptMyRAIIResource = NULL; } T* GetResource() { return m_ptMyRAIIResource; } private: T* m_ptMyRAIIResource; }; #endif //TEST_RAII_2_2017_3_18_SKDJFGHDSDFG_H_
6.MFC画bmp图片
从路径读取bmp图片,画到dc上。
//MFC从文件画BMP图片 void DrawBmpPicture(CDC* pDC) { CFileFind findFile; CString strPicturePath = "E:\\Picture.bmp"; BOOL bFindFile = findFile.FindFile(strPicturePath); //找到BMP图片 if(bFindFile) { CDC dcMem; dcMem.CreateCompatibleDC(pDC); //获取图片句柄 HBITMAP hBmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(), strPicturePath, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION|LR_LOADFROMFILE); CBitmap *pBitmap = CBitmap::FromHandle(hBmp); //获取位图信息 BITMAP bitmap; pBitmap->GetBitmap(&bitmap); //将位图选人到pDC中 CBitmap* pbitold = dcMem.SelectObject(pBitmap); float fLogSize = 0.4; //放大倍数 CRect rectDraw(0, 0, 500, 500); //画图坐标 //以stretchBlt的方式添加位图到相应区域 pDC->StretchBlt(rectDraw.left, rectDraw.top, (int)(bitmap.bmWidth*fLogSize), (int)(bitmap.bmHeight*fLogSize), &dcMem, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY); pBitmap->DeleteObject(); pDC->SelectObject(pbitold); dcMem.DeleteDC(); } }
7.Kill掉指定名称的进程
需要“psapi.lib”,“PSAPI.DLL”和“psapi.h”下载地址:点击打开链接void KillProcessByName(LPCWSTR strProcessName) { //HWND是Windows窗口句柄 HWND hProcessWindow = ::FindWindow(NULL, strProcessName); //进程ID ULONG nProcessProcessID = 0; ::GetWindowThreadProcessId(hProcessWindow, &nProcessProcessID); //HANDLE(句柄),是Windows用来表示对象的 HANDLE hProcessHandle = ::OpenProcess(PROCESS_TERMINATE, FALSE, nProcessProcessID); //杀死进程 ::TerminateProcess(hProcessHandle, 4); }
8.重载new/delete来检测内存泄露,显示内存申请文件和行号
OperatorNewSrc.h#ifndef OPERATOR_NEW_20170609_SDFGDFGF_H_H_ #define OPERATOR_NEW_20170609_SDFGDFGF_H_H_ #include "assert.h" #include <iostream> using namespace std; void * operator new(size_t nSize, const char* pFIle, const size_t unLine); void * operator new[] (size_t nSize, const char* pFIle, const size_t unLine); void operator delete(void *pVoid); void operator delete[](void *pVoid); static int nNewTimes = 0; #endif //OPERATOR_NEW_20170609_SDFGDFGF_H_H_
OperatorNewSrc.cpp#include "stdafx.h" #include "OperatorNewSrc.h" void * operator new(size_t nSize, const char* pFIle, const size_t unLine) { if (nSize <= 0) { return NULL; } ++nNewTimes; void *pVoid = malloc(nSize);//此处的nSize会自动计算为new对象的大小.如:int *pTest = new int(__FILE__,__LINE__);此时nSize=4; if(!pVoid) { assert(pVoid); cout <<endl <<"内存申请失败=> " <<"NewTimes:" <<nNewTimes <<" Size:" <<nSize <<" File:" <<pFIle <<" Line:" <<unLine <<endl; } cout <<endl <<"内存申请成功=> " <<"NewTimes:" <<nNewTimes <<" Size:" <<nSize <<" File:" <<pFIle <<" Line:" <<unLine <<endl; return pVoid; } void * operator new[] (size_t nSize, const char* pFIle, const size_t unLine) { return operator new(nSize); } void operator delete(void *pVoid) { if (pVoid) { free(pVoid); pVoid = NULL; } } void operator delete[](void *pVoid) { operator delete(pVoid); }
测试代码(注:不能用于多线程)#include "stdafx.h" #include "OperatorNewSrc.h" #define new new(__FILE__,__LINE__) //一定记得加上这句 class CTestOperatorNew { public: CTestOperatorNew(); ~CTestOperatorNew() {} private: int m_nTest; char m_chTest; }; CTestOperatorNew::CTestOperatorNew() { m_chTest = 'a'; m_nTest = 789; } int _tmain(int argc, _TCHAR* argv[]) { int *pnTest = new int(__FILE__, __LINE__); int *pszTest = new int(__FILE__, __LINE__); CTestOperatorNew *pTestNew = new CTestOperatorNew; delete pnTest; delete pszTest; delete pTestNew; system("pause"); return 0; }
执行结果