VC++ 开发总结回顾(二)
1.获取项目的路径,有的时候我们需要获得exe项目的路径,可以用vc的GetModuleFileName,再做一下简单的项目切割就可
CString GetModuleDir()
{
HMODULE module = GetModuleHandle(0);
char pFileName[MAX_PATH];
GetModuleFileName(module, pFileName, MAX_PATH);
CString csFullPath(pFileName);
int nPos = csFullPath.ReverseFind(('\\'));
if (nPos < 0)
return CString("");
else
return csFullPath.Left(nPos);
}
2、vc中我们很多时候需要执行一些cmd的命令,但是无论我们用system函数,还是exec 都会产生控制台,我们可以用win32的api CreateProcess完成这个功能
//调用命令行命令而不显示命令行窗口
BOOL CCommonFunction::system_hide(char* CommandLine)
{
SECURITY_ATTRIBUTES sa;
HANDLE hRead, hWrite;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
if (!CreatePipe(&hRead, &hWrite, &sa, 0))
{
return FALSE;
}
STARTUPINFO si;
PROCESS_INFORMATION pi;
si.cb = sizeof(STARTUPINFO);
GetStartupInfo(&si);
si.hStdError = hWrite;
si.hStdOutput = hWrite;
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
//关键步骤,CreateProcess函数参数意义请查阅MSDN
if (!CreateProcess(NULL, CommandLine, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi))
{
return FALSE;
}
CloseHandle(hWrite);
char buffer[4096] = { 0 };
DWORD bytesRead;
while (true)
{
memset(buffer, 0, strlen(buffer));
if (ReadFile(hRead, buffer, 4095, &bytesRead, NULL) == NULL)
break;
//buffer中就是执行的结果,可以保存到文本,也可以直接输出
//printf(buffer);//这行注释掉就可以了
Sleep(100);
}
return TRUE;
}
这里有一个比较精髓的地方就是CreatePipe创建管道,将cmd 返回的内容写入管道里,既可以隐藏cmd控制台的出现,又可以获取返回值的内容
3、有的时候我们需要绘制dialog的背景,需要将dialog的背景绘制成一个图片,我们可以这样处理贴出我以前在百度收集的代码
void CbigScreen15Dlg::PaintBack()
{
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 CDialogEx::OnPaint()
CString strPath;
strPath = GetModuleDir() + "\\resource\\back.jpg";
CFile m_file(strPath, CFile::modeRead);
//获取文件长度
DWORD m_filelen = m_file.GetLength();
//在堆上分配空间
HGLOBAL m_hglobal = GlobalAlloc(GMEM_MOVEABLE, m_filelen);
LPVOID pvdata = NULL;
//锁定堆空间,获取指向堆空间的指针
pvdata = GlobalLock(m_hglobal);
//将文件数据读区到堆中
m_file.Read(pvdata, m_filelen);
IStream* m_stream;
GlobalUnlock(m_hglobal);
//在堆中创建流对象
CreateStreamOnHGlobal(m_hglobal, TRUE, &m_stream);
//利用流加载图像
OleLoadPicture(m_stream, m_filelen, TRUE, IID_IPicture, (LPVOID*)&m_picture);
m_stream->Release();
m_picture->get_Width(&m_width);// 宽高,MM_HIMETRIC 模式,单位是0.01毫米
m_picture->get_Height(&m_height);
m_IsShow = TRUE;
m_file.Close();
if (m_IsShow == TRUE)
{
CRect rect;
GetClientRect(rect);
int nW, nH;
nW = (int)(rect.Width());
nH = (int)(rect.Height());
m_picture->Render(dc, 0, 0, nW, nH, 0, m_height, m_width, -m_height, &rect);
}
}
4.检测windows 电脑 网络接入状况 可以引入头文件
#include <SensAPI.h>
#pragma comment(lib, "Sensapi.lib")
//监控网络
DWORD WINAPI controlProc(LPVOID lpParm)
{
挂起线程
CConnectFlag = TRUE;//初始连接标识符默认位连接成功
while (1)
{
DWORD networkFlag;//网络连接标志位描述符
if (IsNetworkAlive(&networkFlag))//网络连接正常
{
if (!CConnectFlag)
{//之前断过网
CConnectFlag = TRUE;
functionPen.RestartExecute();//重启应用程序
}
}
else {//网络连接不正常
CConnectFlag = FALSE;
}
Sleep(1000);
}
return 0;
}
5.windows 下 连接socket
SOCKET socketFlag = socket(
AF_INET,
SOCK_STREAM,//决定你是用udp还是tcp
IPPROTO_IP
);
if (socketFlag <= 0)
{
CLogFile::WriteLog("创建socket失败");
return TRUE;
}
if (int result = connect(socketFlag, (SOCKADDR*)&socket_server, sizeof(SOCKADDR)) == SOCKET_ERROR) {
CLogFile::WriteLog("连接socket失败");
return 0;
};
6.windows下多线程,有的时候我们写客户端需要操作多线程,因为如果所有任务都在主线程中操作,那么很容易导致exe卡死,所以我们需要在打开一个线程,无论是c的线程还是vc 封装的线程,我们都要明白一个通俗的事情,就是线程之间的操作会对同一块内存的读写产生冲突,再就是windows上的,线程开辟出一块内存空间,实际上不管哪个开发都是又开辟出一个函数而已
windows 下 常用的加锁方式有临界区 互斥体 和 信号量,信号量我用的不多,只用过互斥体和 临界区,当然你一可以封装自己的方法,封装出自动锁也是没问题的
临界区
CRITICAL_SECTION
这里有四个关键函数:InitializeCriticalSection EnterCriticalSection LeaveCriticalSection DeleteCriticalSection来完成此机制
互斥体,Mutex可以与WaitForSingleObject配合使用,常用的互斥体操作函数:
CreateMutex ReleaseMutex
7.创建线程c的有pthread_create等等一系列函数这里不做介绍,只介绍vc中的,毕竟是windows编程:
CWinThread* downloadHandle = AfxBeginThread((AFX_THREADPROC)controlFun, (LPVOID)socketFlag);
//接收线程
void controlFun(SOCKET sockClient)
{
//如果说收到数据了
char data[2048];//定义2M的数据用来存储接收数据
MSG msg;
while (1)
{//收到数据
MsgWaitForMultipleObjects(1, &hMutex, FALSE, INFINITE, QS_ALLINPUT);
char data[2048];//定义2M的数据用来存储接收数据
int byte = recv(sockClient, data, sizeof(data), 0);
if (byte>0) {//收到数据并且未产生处理
lockDonwload = TRUE;//关闭状态防止再次重复处理
CString mRecvData(data);//强转Cstring
int Position;//标记切割位置
/*
*这里可能出现粘包现象这里通过字符串处理切割出一个完整的数据包
*/
while ((Position = mRecvData.ReverseFind('&')) >= 0)
{//切割出一个完整的数据包
CString outInfo;
outInfo.Format("%d", Position);
mRecvData = mRecvData.Left(Position);
}
CCommonFunction commonFunction;
CStringArray downloadInfoArray;
char splitString = '|';
int downloadInfoArraySize = commonFunction.splitString(mRecvData, splitString, downloadInfoArray);
if (downloadInfoArraySize > 0)//如果说存在数字
{
if (!downloadInfoArray[0].IsEmpty())//如果说第一个字符串不是空
{
CString mServer_cmd;//接收服务端的指令
mServer_cmd = downloadInfoArray[0];
CString ourDir = commonFunction.GetExecuteAppDir();//获取程序路径
//下载报纸
if (mServer_cmd == "download_file")
{//服务端发出命令要求下载文件
if (!downloadInfoArray[3].IsEmpty()) {//下载文件的网址
CString downloadFile = ourDir + "\\download\\" + downloadInfoArray[3];//下载文件的位置
CDownloadCtrl ctrlDlg;
//初始化下载需要的参数
ctrlDlg.strUrl = downloadInfoArray[2];//下载链接
ctrlDlg.strSavePath = downloadFile;//存储路径
ctrlDlg.filename = downloadInfoArray[3];//下载文件名称
ctrlDlg.fileSize = atoi(downloadInfoArray[4]);//文件大小
ctrlDlg.version = downloadInfoArray[5];//报纸版本号
ctrlDlg.downloadFile = downloadFile;//要保存的文件
ctrlDlg.identifier = downloadInfoArray[1];//报纸标识
ctrlDlg.downloadSocket = sockClient;
ctrlDlg.downloadTyoe = "报纸版本";//下载报纸
//打开下载进度条
INT_PTR nResponse = ctrlDlg.DoModal();
}
}
}
else {
CLogFile::WriteLog("接收到了空数据");
}
}
else {
CLogFile::WriteLog("服务器返回信息失败");
}
lockDonwload = FALSE;//不处于下载状态了
}
Sleep(1000);
ReleaseMutex(hMutex);
}
return;
}
8.vc的http操作也是非常繁琐的,需要初始化绘画,CHTTP等等一系列操作:
CString strUrl;//下载地址
DWORD dwServiceType, dwRet; //dwServiceType用于保存服务类型,dwRet用于保存提交GET请求返回的状态号
unsigned short nPort; //用于保存目标HTTP服务端口
CString strServer, strObject; //strServer用于保存服务器地址,strObject用于保存文件对象名称
//解析URL,获取信息
if (!AfxParseURL(strUrl, dwServiceType, strServer, strObject, nPort))
{
//解析失败,该Url不正确
return -1;
}
//创建网络连接对象,HTTP连接对象指针和用于该连接的HttpFile文件对象指针,注意delete
//建立网络连接
CHttpConnection *pHtCon = intsess.GetHttpConnection(strServer, nPort);
if (pHtCon == NULL)
{
//建立网络连接失败
intsess.Close();
return -2;
}
//发起GET请求
pHtFile = pHtCon->OpenRequest(CHttpConnection::HTTP_VERB_GET, strObject);
if (pHtFile == NULL)
{
//发起GET请求失败
intsess.Close();
delete pHtCon;pHtCon = NULL;
return -3;
}
if (fileFindHandle.FindFile(strSavePath, 0))
{//如果说存在文件
CFile fileHandle(strSavePath, CFile::modeRead);
CheckFileLength = fileHandle.GetLength();//文件长度
if (CheckFileLength < fileSize)
{
//定义访问类型
CString SZ_HEAD = "Accept: */*\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36\n";
CString strRangeQuest;
mCheckFileExistFlag = TRUE;
strRangeQuest.Format(("%sRange: bytes=%I64d-%I64d\r\n"), SZ_HEAD, CheckFileLength, fileSize);
//将范围加入头信息中
pHtFile->AddRequestHeaders(strRangeQuest);
}
else {
//文件完整
if (CheckFileLength == fileSize) {
fileComplete = TRUE;
return 0;//文件已经下载过了
}
else {
return -11;//文件异常
}
}
}
//提交请求
pHtFile->SendRequest();
//获取服务器返回的状态号
pHtFile->QueryInfoStatusCode(dwRet);
if (dwRet != HTTP_STATUS_OK && dwRet != HTTP_STATUS_PARTIAL_CONTENT)
{
//服务器接收下载状态异常
/*CString status;
status.Format("%d", dwRet);
AfxMessageBox(status);
intsess.Close();*/
delete pHtCon;pHtCon = NULL;
delete pHtFile;pHtFile = NULL;
return -4;
}
9.最后是windows的消息泵了,vc编程需要了解windows 的消息机制,比如说我们写一个while循环,如果说不做消息泵处理,你写的exe基本会开始,因为while循环没有给cpu切片时间,而且阻塞了windows的消息传递,所以导致了软件的卡死
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
break;
if (msg.message == WM_PAINT) {
//防止死循环卡死
PAINTSTRUCT ps;
HWND hWnd;
hWnd = this->m_hWnd; //CWnd->HWND
::BeginPaint(hWnd,&ps);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
.....................
}