十年编程经验分享(转)
经验分享,如何写线程
写线程我想每人不会吧,我只是谈谈我自己的经验
首先一定不要使用beginthread,其次最好也不使用CreateThread,看MSDN讲最好用_beginthread或者_beginthreadex
写线程函数,我个人的经验是把线程函数写在类里,而且写出protected成员函数,而不是写成一个单独的函数,原因是:
1。线程函数一般都是和类有关系
2。写成类的函数,这样在线程函数里就可以调用类的成员变量。如果写成一个单独的函数,就不能直接调用类的私有成员变量了
3。写成protected成员函数,可以供继承类调用
另外我想大家写线程的参数传递方法都差不多,就是把类的指针传进去
有时候我们需要传递其他的值,这时候我习惯是在cpp文件中定义一个全局的类指针,在线程函数里直接使用
经验分享,如何写DLL
DLL一般有2种写法。
第一种用MS.NET的向导帮助就能生产一个DLL,很简单。也就是有个类的头文件,写好DLL后把DLL和头文件给调用端一起使用。
第二种是写一个接口文件。
在这里,我介绍一下通过接口文件写DLL。接口文件主要就是用到纯虚类。(我以前明白什么是纯虚类,但就是不知道怎么用。也看过关于接口的介绍,但没有看到实际的例子)
MSDN关于DLL也提到一句话,就是通过接口来写,但具体怎么写就没说了。
我们需要一个接口文件(h文件),一个实现接口的类的文件(h和cpp文件或者只有h文件)
接口文件名一般用“I”开头,实现接口的类的文件一般用Impl结尾。例如:
//---------------------接口文件 IMyDLL.h ------
//---------------------接口文件 IMyDLL.h ------
struct IMyDLL
{
virtual void Init() = 0;
}
//-------------------实现文件 MyDLLImpl.h --------
//继承IMyDLL
#include "IMyDLL.h"
class CMyDLLImpl : public IMyDLL
{
virtual void Init();
}
//-----------------实现文件 MyDLLImpl.cpp --------
#include "MyDLLImpl.h"
void CMyDLLImpl::Init()
{
; //真正实现的代码
}
现在剩下的事情就是写DLL文件了
//-------------DLL文件 MyDLL.h ---------------
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
MYDLL_API void * CreateIMyDLL();
//------------DLL文件 MyDLL.cpp --------------
#include "MyDLL.h"
#include "MyDLLImpl.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
MYDLL_API void * CreateIMyDLL()
{
CMyDLLImpl * pMyDLLImpl = new CMyDLLImpl();
return static_cast <void *> (pMyDLLImpl);
}
这样基本上就写完了。调用端使用的时候只要DLL的头文件(MyDLL.h),库文件和接口文件(IMyDLL.h)
为什么要使用接口写DLL,一个原因也就是MSDN说的在某些时候如果你给调用端一个类的头文件,可能这个头文件需要include很多其他的文件,对调用端并不合适。
我自己也碰到过类似的问题,调用端不能编译,后来改成接口文件就好了。
使用接口文件,就没有其他的什么文件。另外一个原因就是接口设计自身的好处了,这里就不说了。
还要记得在接口文件中定义一个Release函数。供调用端释放内存
Release函数很简单,就是写
delete this;
经验分享,如何使用try,catch, throw之一
try,catch, throw的含义就很清楚,但怎么使用,我还是过了好多年才明白一些
先看一个不好的例子
//----------------- 不好的代码 -----------------
//----------------- 不好的代码 -----------------
class CTest1;
class CTest2;
class CTest3;
void BadCode()
{
//new test1
CTest1 * pTest1 = new CTest1;
//do something
bool bRet = DoSomething();
if (!bRet)
{
delete pTest1;
return;
}
//new CTest2
CTest2 * pTest2 = new CTest2;
//do something
bRet = DoSomething();
if (!bRet)
{
delete pTest1;
delete pTest2;
return;
}
//new CTest3
CTest3 * pTest3 = new CTest3;
//do something
bRet = DoSomething();
if (!bRet)
{
delete pTest1;
delete pTest2;
delete pTest3;
return;
}
//release
delete pTest1;
delete pTest2;
delete pTest3;
}
下面是我个人比较喜欢的写法
//--------- 好的例子 ---------------
//--------- 好的例子 ---------------
class CTest1;
class CTest2;
class CTest3;
void MyCode()
{
//define
CTest1 * pTest1 = NULL;
CTest2 * pTest2 = NULL;
CTest3 * pTest3 = NULL;
//使用try, catch, throw
try
{
//new test1
pTest1 = new CTest1;
//do something
bool bRet = DoSomething();
if (!bRet)
throw -1;
//new CTest2
pTest2 = new CTest2;
//do something
bRet = DoSomething();
if (!bRet)
throw -2;
//new CTest3
pTest3 = new CTest3;
bRet = DoSomething();
//do something
if (!bRet)
throw -3;
//release
delete pTest1;
pTest1 = NULL;
delete pTest2;
pTest2 = NULL;
delete pTest3;
pTest3 = NULL;
}
catch()
{
if (pTest1)
delete pTest1;
if (pTest2)
delete pTest2;
if (pTest3)
delete pTest3;
}
}
另外在一个函数里不仅仅只有一个try,catch。可以有多个。比如:
//--------- 多个try的例子 ---------------
class CTest1;
class CTest2;
class CTest3;
void MyCode()
{
//define
CTest1 * pTest1 = NULL;
CTest2 * pTest2 = NULL;
CTest3 * pTest3 = NULL;
//使用try, catch, throw
try
{
//new test1
pTest1 = new CTest1;
//do something
bool bRet = DoSomething();
if (!bRet)
throw -1;
}
catch()
{
;
}
if (pTest1)
delete pTest1;
try
{
//new CTest2
pTest2 = new CTest2;
//do something
bRet = DoSomething();
if (!bRet)
throw -2;
}
catch()
{
;
}
if (pTest2)
delete pTest2;
try
{
//new CTest3
pTest3 = new CTest3;
bRet = DoSomething();
//do something
if (!bRet)
throw -3;
}
catch()
{
;
}
if (pTest3)
delete pTest3;
}
简单说一下
第一种写法,需要在不同的地方delete 不同的变量
第二种写法,在catch里delete所有的变量,代码的结构看起来更容易读,也易于维护
//define
CTest1 * pTest1 = NULL;
CTest2 * pTest2 = NULL;
CTest3 * pTest3 = NULL;
变量定义一定要在try之前定义,否则catch里找不到这些变量的定义
另外很重要的一点,在delete之后必须将变量设成NULL
catch里的delete可以删掉
把try里的delete放catch后,因为指针都初始化NULL了
经验分享,如何使用try,catch, throw之二
什么时候使用try,catch,什么时候不用;什么时候用throw,什么时候不用。工作了很多年才明白。
我个人的理解是:
1。在private或者protected的成员函数不使用try,catch,而只使用throw
2。如果在private或者protected的成员函数需要使用try,catch,那么就要使用rethrow
3。在public成员函数里使用try,catch
4。如果该类相对于整个项目来说是属于被调用层,那么public成员函数也可以不使用try,catch
5。如果调用第三方的代码,我一般都会用try,catch
我个人的习惯是把private或者protected成员函数的名字使用前缀__,public函数不用
先看一个我不喜欢的写法
//------------- try, catch, throw 例子,不喜欢的写法 ------------
//------------- try, catch, throw 例子,不喜欢的写法 ------------
class CTest
{
public:
int Init();
private:
int __InitA();
int __InitB();
int __InitC();
}
//--------- Init ------------
int CTest:Init()
{
try
{
int err;
err = __InitA();
if (err != 1)
throw -1;
err = __InitB();
if (err != 1)
throw -2;
err = __InitC();
if (err != 1)
throw 3;
return 1;
}
catch(int & err)
{
return err;
}
}
//---------- __InitA ----------
int CTest::__InitA()
{
try
{
int err;
err = DoSomething1();
if (err != 1)
throw -1;
err = DoSomething2();
if (err != 1)
throw -2;
return 1;
}
catch(int & err)
{
return err;
}
}
__InitB, ___InitC和___InitA类似
下面是我个人比较喜欢的写法
C/C++ code
//------------- try, catch, throw 例子,喜欢的写法 ------------
class CTest
{
public:
int Init();
private:
int __InitA();
int __InitB();
int __InitC();
}
//--------- Init ------------
int CTest:Init()
{
try
{
__InitA();
__InitB();
__InitC();
return 1;
}
catch(int & err)
{
return err;
}
}
//---------- __InitA ----------
int CTest::__InitA()
{
int err;
err = DoSomething1();
if (err != 1)
throw -1;
err = DoSomething2();
if (err != 1)
throw -2;
return 1;
}
__InitB, ___InitC和___InitA类似
我喜欢的写法是private或者protected函数没有try,catch,只有throw
在public函数就不需要去判断每个private或者protected函数的返回值了
看起来会更清楚一点
如果在private或者protected的成员函数需要使用try,catch,那么就要再throw,而不是调用return
回想一下windows的win32函数,返回有2种方式
一种是返回一个错误值
另外一种就是抛出异常
在我自己的设计中,什么时候返回值什么时候抛出异常,很值得推敲一下
另外,仔细考虑一下private和protected函数,它们永远都会返回1或者在它们出错的时候就会throw,那在写函数声明的时候也就不需要返回值了,就返回void就可以了,也就不需要写return语句了。
然后,在某些情况下,有时候我们真的需要private或者protected函数返回错误代码,而不是throw,这个时候如果再声明函数的时候有返回值,就可以和没有返回值的函数区别开了。
经验分享,代码编写习惯
以下是我个人的习惯:
1。将所有的错误代码返回值写在一个文件里,使用define来定义错误代码
2。写几const来定义操作错误代码,比如:
typedef int MY_ERR;
const int MY_UNKNOW_ERR 0
const int MY_OK 1
#define MY_SUCCEEDED(Status) ((MY_ERR)(Status) == MY_OK)
#define MY_FAILED(Status) ((MY_ERR)(Status) != MY_OK)
3。函数的返回值基本上都是MY_ERR,或者是void,很少有BOOL。因为很多情况下我们需要知道函数错在哪里,错误代码是什么。
函数返回值是MY_ERR,而不是int。这样表示函数返回的是一个错误代码。如果函数返回int,那就表示函数的确是返回一个int的数值
4。如何写类函数,请参考我的帖子"经验分享,如何使用try,catch, throw之二"
5。基本上调用public函数的写法是:
MY_ERR err = myclass.test1();
if MY_FAILED(err)
throw err;
6。写一个错误消息映射文件,即每个错误代码映射一窜字符。字符可以是多语言的。一般写在xml文件里。这样根据每个函数的返回值,就可以得到映射的字符串。
7。另外会有一个专门写日志的文件,日志文件有两种,一个是给用户看的操作日志,一个是给自己看的调试日志
8。尽量不用在底层的代码里写MessageBox这样的函数。一个项目在哪里弹出对话框,大多少情况下都是由GUI来决定的,不是由底层的代码来决定的。
9。提供一个common或者叫public的类,提供所有项目都可以使用的函数或者类
10。另外就是总所周知的事情,使用N层系统去做代码设计
日志有两种,一种是操作日志,一种是调试日志
操作日志是记录用户每一步的操作情况,也不一定要写的很详细,也不一定要记录每一步
调试日志是你认为需要记录的内容,比如一些变量的值。
调试日志你可以写个工具实时查看,这样有一个好处,可以实时调试程序,
在一些情况下,我们不能用.NET的调试工具,我们需要实时运行程序,不能用断点中断调试,这时候用调试日志就很有用。
比如在调试线程的时候,有时候使用断点是不能得到正确结果的。
我个人的习惯是调试信息写成“【类名】【方法名】调试信息”
另外对于错误的信息,不仅要写到日志里,还要考虑是否弹出对话框给用户
错误代码的使用
先看一个不好的例子
先看一个不好的例子
C/C++ code
//------ 不好的例子 -------
void Test()
{
if (xxx1)
{
MessageBox(xxx);
return;
}
if (xxx2)
{
MessageBox(xxx);
return;
}
if (xxx3)
{
MessageBox(xxx);
return;
}
}
我个人喜欢的写法是:
void Test()
{
try
{
if (xxx1)
throw ERROR_CODE_1;
if (xxx2)
throw ERROR_CODE_2;
if (xxx3)
throw ERROR_CODE_3;
}
catch(ERROR_CODE & err)
{
TCHAR * tchMsg = GetMessage(err);
MessageBox(tchMes);
}
}
提供一个函数,通过error code得到错误信息
我在写下位机程序,并且下位机内存有限,个人认为要尽量使用#define,不知道对不对。
我在写下位机程序时就碰到内存不够的问题,将const改成#define就好了
另外在使用enum定义错误代码的时候,有时候需要从enum转换到int,自己感觉有点麻烦
所以自己也就使用宏定义了,不过是一定要小心使用
线程函数写成类的protected函数
如果这个函数不是属于类的,那么就不能访问类的私有成员变量
现在的前提是这个函数是类的函数,它就可以访问类的所有成员变量
换一个角度,你先设计好了一个类,后来发现这个类的其中一个方法在线程下运行更合适,这个时候就把这个函数设计成线程调用的函数
并且,这个函数仍然是类的一个成员函数,你不能因为它在线程下运行更合适,就更改你的类的设计
线程是操作系统的概念,不是类的概念
经验分享,C语言代码编写习惯
C++可以利用try, catch, throw来写代码风格
C语言没有, 我个人使用goto语句来写代码风格
大家都知道最好不要使用goto, 但不是完全不要使用goto语句
函数设计有一个原则, 就是一个函数只有一个出口, 也就是只有一个地方有return
Code
先看一个不好的例子
C/C++ code
//--------- 不好的例子 ----------
boolean fun1()
{
if (__funx1() != 0)
return false;
if (__funx2() != 0)
return false;
if (__funx3() != 0)
return false;
return true;
}
以下是我个人喜欢的风格
//--------- 我个人喜欢的风格 ----------
//定义一个全局变量
int g_nErrorCode = 0;
//在函数__funx1, __funx2, __funx3中如果出错, 会设置g_nErrorCode值
//这个函数会有两个出口, 一个return true; 一个return false;
boolean fun1()
{
if (__funx1() != 0)
goto error;
if (__funx2() != 0)
goto error;
if (__funx3() != 0)
goto error;
return true;
error:
return false;
}
//如果要这个函数只有一个出口, 可以按以下的代码去写
boolean fun1()
{
bool bRet = true;
if (__funx1() != 0)
goto error;
if (__funx2() != 0)
goto error;
if (__funx3() != 0)
goto error;
//这句话永远都不能成立, 只有goto语句能进入
if (false)
{
error:
bRet = false;
}
return bRet;
}
经验分享,串口的通讯结构代码
我在工作中写过串口调用的代码,我想我写的代码应该还算是比较规范,可以给大家做一个参考
在代码中会用到一个叫SAMA.dll的文件, 关于它的源代码可以去下载,下载地址是
http://d.download.csdn.net/down/1150752/zyx040404
我写的这个串口封装调用是用在探针台的自动上下片系统里, 串口的封装代码在SAMA里, 此串口的通讯代码的结构是:
ILoader.h接口文件, 定义了上位机和下位机发送命令的函数等等
LoaderImpl.h, LoaderImpl.cpp 继承了ILoader.h文件, 是实现体
LoaderMCUDataDefine.h定义了上下位机共同使用的数据结构和命令格式代码以及错误代码,上下位机的命令格式是第一个字节是0xFF,第二和第三个是重复的命令字节,这样可以避免收到错误的串口数据
LoaderDataDefine.h定义了上位机的一些数据结构和消息定义,这些消息将发送给主窗口,由主窗口做相应的处理
我现在写上部分代码
Code
//ILoader.h
C/C++ code
#pragma once
#include "../SAMA/SAMADataDefine.h"
#include "LoaderDataDefine.h"
#include "LoaderErrorCode.h"
struct ILoader
{
//relese
virtual inline void Release() = 0;
//Initialize
virtual COMMON_ERR Initialize(const TCHAR * tchConfigFile) = 0;
//送消息的窗口句柄
virtual inline void SetNotifyWindow(HWND hWnd) = 0;
//send command to low machine
virtual inline COMMON_ERR SendCommandToLowMachineEx(BYTE iCommand, int iValue, BOOL bDirection) = 0;
//send other command to low machine
virtual inline COMMON_ERR SendCommandToLowMachine(BYTE iCommand, DWORD dwWaitTime = LOADER_MIN_WAIT_LOW_MACHINE_RETURN_TIME, BOOL bIsForcedSend = FALSE, BOOL bWaitResult = FALSE) = 0;
//set comm infromation
virtual inline COMMON_ERR SetCommInformation() = 0;
//update low machine para
virtual inline COMMON_ERR GetLowMachinePara() = 0;
virtual inline COMMON_ERR UpdateLowMachinePara(const tagLoaderPara & stuLoaderPara) = 0;
virtual inline BOOL IsWorking() const = 0;
virtual inline BOOL IsWarning() const = 0;
virtual inline int GetErrorCode() const = 0;
virtual inline int GetErrorValue() const = 0;
virtual inline COMMON_ERR IsValid() const = 0;
};
//LoaderImpl.h
//LoaderImpl.h
#pragma once
#include "ILoader.h"
#include "../SAMA/comm/CComm.h"
class CLoaderImpl : public ILoader, public CAsynThreadComm
{
public:
CLoaderImpl(void);
virtual ~CLoaderImpl(void);
//relese
virtual inline void Release();
//Initialize
virtual COMMON_ERR Initialize(const TCHAR * tchConfigFile);
//送消息的窗口句柄
virtual inline void SetNotifyWindow(HWND hWnd);
//send command to low machine
virtual inline COMMON_ERR SendCommandToLowMachineEx(BYTE iCommand, int iValue, BOOL bDirection);
//send command to low machine
virtual inline COMMON_ERR SendCommandToLowMachine(BYTE iCommand, DWORD dwWaitTime = LOADER_MIN_WAIT_LOW_MACHINE_RETURN_TIME, BOOL bIsForcedSend = FALSE, BOOL bWaitResult = FALSE);
//set comm information
virtual inline COMMON_ERR SetCommInformation();
//update low machine para
virtual inline COMMON_ERR GetLowMachinePara();
virtual inline COMMON_ERR UpdateLowMachinePara(const tagLoaderPara & stuLoaderPara);
virtual inline BOOL IsWorking() const;
virtual inline BOOL IsWarning() const;
virtual inline int GetErrorCode() const;
virtual inline int GetErrorValue() const;
virtual inline COMMON_ERR IsValid() const;
private:
HANDLE m_hWaitResult;
BOOL m_bIsWorking; //是否正在工作
BOOL m_bIsWarning; //是否正在报警
int m_nErrorCode;
int m_nErrorValue;
tagLoaderPara m_stuLoaderPara;
inline COMMON_ERR __SendCommandToLowMachineOnForce(BYTE iCommand, DWORD dwWaitTime = LOADER_MIN_WAIT_LOW_MACHINE_RETURN_TIME, BOOL bWaitResult = FALSE);
protected:
virtual void __OnReceive();
};
//LoaderImpl.cpp
//LoaderImpl.cpp
#include "StdAfx.h"
#include ".\Loaderimpl.h"
#include "../SAMA/SAMA.h"
#include "LogDefine.h"
#include "../SAMA/SAMAXML/IXMLParser.h"
#include <process.h>
#include <math.h>
static CLoaderImpl * l_pLoader = NULL;
//------------------------------------------------------------------------------------------------------
// CLoaderImpl
//------------------------------------------------------------------------------------------------------
CLoaderImpl::CLoaderImpl(void)
{
m_hWaitResult = CreateEvent(NULL, FALSE, FALSE, NULL);
m_bIsWorking = FALSE;
m_bIsWarning = FALSE;
m_nErrorCode = LOADER_ERROR_NO_ERROR;
m_nErrorValue = 0;
memset(&m_stuLoaderPara, NULL, sizeof(m_stuLoaderPara));
}
//------------------------------------------------------------------------------------------------------
// ~CLoaderImpl
//------------------------------------------------------------------------------------------------------
CLoaderImpl::~CLoaderImpl(void)
{
CloseHandle(m_hWaitResult);
}
//------------------------------------------------------------------------------------------------------
// Release
//------------------------------------------------------------------------------------------------------
inline void CLoaderImpl::Release()
{
delete this;
}
//------------------------------------------------------------------------------------------------------
// __OnReceive
//------------------------------------------------------------------------------------------------------
void CLoaderImpl::__OnReceive()
{
static BYTE byteAryMsg[MCU_SEND_BUFFER_SIZE];
memset(byteAryMsg, NULL, sizeof(byteAryMsg));
//business process
COMMON_ERR err = COMMON_OK;
size_t iLen = 0;
BOOL bIsWorking = FALSE;
try
{
//read comm
iLen = Read((char *) byteAryMsg, 1);
if (0 == iLen)
throw ERROR_LOADER_CAN_NOT_READ_SERIEAL_DATA;
if (byteAryMsg[0] != LOADER_H2L_ACTION)
throw ERROR_LOADER_IT_IS_NOT_ACTION;
//read receive command
iLen = Read((char *) byteAryMsg, 2);
if (byteAryMsg[0] != byteAryMsg[1])
throw ERROR_LOADER_IT_IS_NOT_COMMAND;
}
catch ()
{
//LOADER_LOG_DEBUG3(_T("[__OnReceive] error code: %d, byteAryMsg[0]: %d, byteAryMsg[1]: %d"), err, byteAryMsg[0], byteAryMsg[1]);
return;
}
LOADER_LOG_INFO1(_T("收到:%d"), byteAryMsg[0]);
try
{
switch(byteAryMsg[0])
{
case LOADER_H2L_GET_ERROR_CODE:
iLen = Readex((char *) byteAryMsg, 6);
m_nErrorCode = byteAryMsg[0];
m_nErrorValue = byteAryMsg[1];
LOADER_LOG_INFO1(_T("错误: %d"), m_nErrorCode);
//need set it now before post message,
//or caller will receive message first, and get working status later, it's wrong
m_bIsWorking = FALSE;
if (m_nErrorCode != LOADER_ERROR_NO_ERROR)
{
m_bIsWarning = TRUE;
if (m_hNotifyWindow)
PostMessage(m_hNotifyWindow, WM_LOADER_WARNING, (WPARAM) m_nErrorCode, NULL);
}
break;
case LOADER_H2L_UPDATE_PARA:
//m_bIsGetLowMachinePara = TRUE;
//send result
if (m_hNotifyWindow)
PostMessage(m_hNotifyWindow, WM_LOADER_UPDATE_PARA, NULL, NULL);
break;
case LOADER_H2L_GET_PARA:
//read data number
iLen = Readex((char *) byteAryMsg, 2);
{
//read data
int i = byteAryMsg[0] * 256 + byteAryMsg[1];
iLen = Readex((char *) byteAryMsg, i);
i = 0;
m_stuLoaderPara.nWaitVacuumTime = byteAryMsg[i] * 256 + byteAryMsg[i + 1];
i = i + 2;
}
//need set it now before post message,
//or caller will receive message first, and get working status later, it's wrong
m_bIsWorking = FALSE;
//send result
if (m_hNotifyWindow)
PostMessage(m_hNotifyWindow, WM_LOADER_GET_PARA, (WPARAM) &m_stuLoaderPara, NULL);
default:
break;
}
}
catch ()
{
LOADER_LOG_DEBUG1(_T("[__OnReceive] unknow error: %d"), GetLastError());
}
m_bIsWorking = bIsWorking;
ClearReceiveBuffer();
SetEvent(m_hWaitResult);
//接着LoaderImpl.cpp
//接着LoaderImpl.cpp
//------------------------------------------------------------------------------------------------------
// Initialize
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::Initialize(const TCHAR * tchConfigFile)
{
LOADER_LOG_INFO(_T("上下片系统初始化"));
l_pLoader = this;
try
{
if (g_pILogger)
g_pILogger->SetLogWindowName(_T("Loader Log"));
//init comm port
SetThreadPriority(THREAD_PRIORITY_NORMAL);
//if (!Open(m_stuLoaderSerial.nPort, m_stuLoaderSerial.nBaudRate))
// throw ERROR_LOADER_CAN_NOT_OPEN_COMM_PORT;
LOADER_LOG_DEBUG(_T("[Initialize] 自动上下片初始化成功"));
return COMMON_OK;
}
catch (COMMON_ERR & err)
{
LOADER_LOG_DEBUG1(_T("[Initialize] error code is : %d"), err);
throw err;
}
}
//------------------------------------------------------------------------------------------------------
// SendCommandToLowMachineEx
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::SendCommandToLowMachineEx(BYTE iCommand, int iValue, BOOL bDirection)
{
COMMON_ERR err = IsValid();
if COMMON_FAILED(err)
return err;
LOADER_LOG_INFO1(_T("发送:%d"), iCommand);
BYTE byteAryMsg[12] = {0};
byteAryMsg[0] = LOADER_H2L_ACTION;
byteAryMsg[1] = byteAryMsg[2] = iCommand;
if (bDirection)
byteAryMsg[3] = 1;
else
byteAryMsg[3] = 0;
byteAryMsg[4] = int(iValue / 256);
byteAryMsg[5] = iValue % 256;
byteAryMsg[6] = 0;
byteAryMsg[7] = 0;
byteAryMsg[8] = byteAryMsg[3] ^ byteAryMsg[4] ^ byteAryMsg[5] ^ byteAryMsg[6] ^ byteAryMsg[7];
byteAryMsg[9] = byteAryMsg[10] = byteAryMsg[11] = LOADER_H2L_END;
Write((char *) byteAryMsg, 12);
return COMMON_OK;
}
//------------------------------------------------------------------------------------------------------
// __SendCommandToLowMachineOnForce
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::__SendCommandToLowMachineOnForce(BYTE iCommand, DWORD dwWaitTime, BOOL bWaitResult)
{
return SendCommandToLowMachine(iCommand, dwWaitTime, TRUE, bWaitResult);
}
//------------------------------------------------------------------------------------------------------
// SendCommandToLowMachine
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::SendCommandToLowMachine(BYTE iCommand, DWORD dwWaitTime, BOOL bIsForcedSend, BOOL bWaitResult)
{
if (!bIsForcedSend)
{
COMMON_ERR err = IsValid();
if COMMON_FAILED(err)
return err;
}
if (bWaitResult)
ResetEvent(m_hWaitResult);
m_bIsWorking = TRUE;
LOADER_LOG_INFO1(_T("发送:%d"), iCommand);
BYTE byteAryMsg[6] = {0};
byteAryMsg[0] = LOADER_H2L_ACTION;
byteAryMsg[1] = byteAryMsg[2] = iCommand;
byteAryMsg[3] = byteAryMsg[4] = byteAryMsg[5] = LOADER_H2L_END;
Write((char *) byteAryMsg, 6);
//wait
if (bWaitResult)
{
DWORD dwRet = WaitForSingleObject(m_hWaitResult, dwWaitTime);
if (WAIT_OBJECT_0 != dwRet)
{
m_nErrorCode = ERROR_LOADER_WAIT_OVER_TIME;
return m_nErrorCode;
}
}
else
Sleep(dwWaitTime);
return COMMON_OK;
}
//------------------------------------------------------------------------------------------------------
// GetErrorData
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::GetErrorData()
{
return __SendCommandToLowMachineOnForce(LOADER_H2L_GET_ERROR_CODE);
}
//------------------------------------------------------------------------------------------------------
// GetLowMachinePara
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::GetLowMachinePara()
{
return __SendCommandToLowMachineOnForce(LOADER_H2L_GET_PARA);
}
//------------------------------------------------------------------------------------------------------
// UpdateLowMachinePara
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::UpdateLowMachinePara(const tagLoaderPara & stuLoaderPara)
{
LOADER_LOG_INFO1(_T("发送:%d"), LOADER_H2L_UPDATE_PARA);
//m_bIsGetLowMachinePara = FALSE;
m_stuLoaderPara.nWaitVacuumTime = stuLoaderPara.nWaitVacuumTime;
BYTE byteAryMsg[1000] = {0};
byteAryMsg[0] = LOADER_H2L_ACTION;
byteAryMsg[1] = byteAryMsg[2] = LOADER_H2L_UPDATE_PARA;
int i = 3;
byteAryMsg[i] = m_stuLoaderPara.nWaitVacuumTime / 256;
byteAryMsg[i + 1] = m_stuLoaderPara.nWaitVacuumTime % 256;
i = i + 2;
byteAryMsg[i] = byteAryMsg[i + 1] = byteAryMsg[i + 2] = LOADER_H2L_END;
Write((char *) byteAryMsg, i + 3);
return COMMON_OK;
}
//------------------------------------------------------------------------------------------------------
// SetCommInformation
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::SetCommInformation()
{
ConfigDialog();
return COMMON_OK;
}
//------------------------------------------------------------------------------------------------------
// IsWorking
//------------------------------------------------------------------------------------------------------
inline BOOL CLoaderImpl::IsWorking() const
{
return m_bIsWorking;
}
//------------------------------------------------------------------------------------------------------
// IsWarning
//------------------------------------------------------------------------------------------------------
inline BOOL CLoaderImpl::IsWarning() const
{
return m_bIsWarning;
}
//------------------------------------------------------------------------------------------------------
// GetErrorCode
//------------------------------------------------------------------------------------------------------
inline int CLoaderImpl::GetErrorCode() const
{
return m_nErrorCode;
}
//------------------------------------------------------------------------------------------------------
// GetErrorValue
//------------------------------------------------------------------------------------------------------
inline int CLoaderImpl::GetErrorValue() const
{
return m_nErrorValue;
}
//------------------------------------------------------------------------------------------------------
// IsValid
//------------------------------------------------------------------------------------------------------
inline COMMON_ERR CLoaderImpl::IsValid() const
{
COMMON_ERR err = COMMON_OK;
if (m_bIsWorking)
{
LOADER_LOG_INFO(_T("正忙"));
err = ERROR_LOADER_IS_WORKING;
}
if (m_bIsWarning)
{
LOADER_LOG_INFO(_T("报警"));
err = GetErrorCode();
}
if COMMON_FAILED(err)
{
if (m_hNotifyWindow)
PostMessage(m_hNotifyWindow, WM_LOADER_WARNING, (WPARAM) err, NULL);
}
return err;
}
//------------------------------------------------------------------------------------------------------
// SetNotifyWindow
//------------------------------------------------------------------------------------------------------
inline void CLoaderImpl::SetNotifyWindow(HWND hWnd)
{
m_hNotifyWindow = hWnd;
}
//LoaderMCUDataDefine.h
//LoaderMCUDataDefine.h
#ifndef LOADER_DATA_DEFINE_H
#define LOADER_DATA_DEFINE_H
struct tagLoaderPara
{
int nWaitVacuumTime; //等待真空时间
};
//------------------------------------------------------------------------------------------------------
// high machine to low machine command
//------------------------------------------------------------------------------------------------------
#define LOADER_H2L_END 255 //end
//those commands need not para
#define LOADER_H2L_ACTION 255 //action
#define LOADER_H2L_GET_ERROR_CODE 40 //get error code
//those commands need para
#define LOADER_H2L_UPDATE_PARA 206 //update para
//------------------------------------------------------------------------------------------------------
// error code
//------------------------------------------------------------------------------------------------------
#define LOADER_ERROR_NO_ERROR 0 //没有错误
#define LOADER_ERROR_UNKNOW_ERROR 255 //未知错误
//LoaderDataDefine.h
//LoaderDataDefine.h
#pragma once
#include "../SAMA/SAMADataDefine.h"
#include "../LoaderMCU/LoaderMCUDataDefine.h"
#define ID_TAGNAME_LOADER_SERIAL _T("串口")
#define ID_TAGNAME_LOADER_SERIAL_PORT _T("端口")
#define ID_TAGNAME_LOADER_SERIAL_BAUD_RATE _T("波特率")
//message
#define WM_LOADER_WARNING WM_USER + 800
#define WM_LOADER_PARA_IS_NOT_RIGHT WM_USER + 801
#define WM_LOADER_GET_PARA WM_USER + 807
#define WM_LOADER_UPDATE_PARA WM_USER + 814
#define LOADER_DEFAULT_START_WAFER_INDEX -1
#define LOADER_MAX_WAIT_LOW_MACHINE_RETURN_TIME 60000 //20 seconds
#define LOADER_MIN_WAIT_LOW_MACHINE_RETURN_TIME 600 //0.2 seconds
//还有一个是LoaderErrorCode.h
//还有一个是LoaderErrorCode.h
#pragma once
//[1000-1990] Loader
const int ERROR_LOADER_CAN_NOT_OPEN_COMM_PORT = 1000; //打开串口错误
const int ERROR_LOADER_INIT_FAILED = 1001; //自动上下片初始化错误
const int ERROR_LOADER_IS_INVALED = 1002; //上下片不可用
const int ERROR_LOADER_IS_WORKING = 1003; //自动上下片正在工作
const int ERROR_LOADER_IS_WARNING = 1004; //自动上下片报警
const int ERROR_LOADER_WAIT_OVER_TIME = 1030; //等待串口超时
const int ERROR_LOADER_CAN_NOT_READ_SERIEAL_DATA = 1032; //没有读到串口数据
const int ERROR_LOADER_IT_IS_NOT_ACTION = 1033; //不是下位机命令头
const int ERROR_LOADER_IT_IS_NOT_COMMAND = 1034; //不是下位机命令
该项目会生成一个dll文件,供主程序去调用
有关写日志的代码请参考SAMA,在我的资源里有下载
http://download.csdn.net/source/1150752
解释一下
SendCommandToLowMachine
SendCommandToLowMachineEx是发送命令并且带一个参数
__SendCommandToLowMachineOnForce是直接发送,不需要检查是否可以发送的状态,上面两个需要检查
参数dwWaitTime是等待下位机回复的时间
参数bIsForcedSend是否不需要检测状态直接发送
参数bWaitResult是否需要等待下位机一定要回复信息,这里会有一个事件对象
UpdateLowMachinePara是发送多个参数给下位机
__OnReceive需要仔细看看,前面一段是判断是不是我们规定的命令格式,中间是命令的处理
如果需要通知主窗口,还会发消息给主窗口,注意m_bIsWorking的付值时间和条件
写线程我想每人不会吧,我只是谈谈我自己的经验
首先一定不要使用beginthread,其次最好也不使用CreateThread,看MSDN讲最好用_beginthread或者_beginthreadex
写线程函数,我个人的经验是把线程函数写在类里,而且写出protected成员函数,而不是写成一个单独的函数,原因是:
1。线程函数一般都是和类有关系
2。写成类的函数,这样在线程函数里就可以调用类的成员变量。如果写成一个单独的函数,就不能直接调用类的私有成员变量了
3。写成protected成员函数,可以供继承类调用
另外我想大家写线程的参数传递方法都差不多,就是把类的指针传进去
有时候我们需要传递其他的值,这时候我习惯是在cpp文件中定义一个全局的类指针,在线程函数里直接使用
经验分享,如何写DLL
DLL一般有2种写法。
第一种用MS.NET的向导帮助就能生产一个DLL,很简单。也就是有个类的头文件,写好DLL后把DLL和头文件给调用端一起使用。
第二种是写一个接口文件。
在这里,我介绍一下通过接口文件写DLL。接口文件主要就是用到纯虚类。(我以前明白什么是纯虚类,但就是不知道怎么用。也看过关于接口的介绍,但没有看到实际的例子)
MSDN关于DLL也提到一句话,就是通过接口来写,但具体怎么写就没说了。
我们需要一个接口文件(h文件),一个实现接口的类的文件(h和cpp文件或者只有h文件)
接口文件名一般用“I”开头,实现接口的类的文件一般用Impl结尾。例如:
//---------------------接口文件 IMyDLL.h ------
//---------------------接口文件 IMyDLL.h ------
struct IMyDLL
{
virtual void Init() = 0;
}
//-------------------实现文件 MyDLLImpl.h --------
//继承IMyDLL
#include "IMyDLL.h"
class CMyDLLImpl : public IMyDLL
{
virtual void Init();
}
//-----------------实现文件 MyDLLImpl.cpp --------
#include "MyDLLImpl.h"
void CMyDLLImpl::Init()
{
; //真正实现的代码
}
现在剩下的事情就是写DLL文件了
//-------------DLL文件 MyDLL.h ---------------
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
MYDLL_API void * CreateIMyDLL();
//------------DLL文件 MyDLL.cpp --------------
#include "MyDLL.h"
#include "MyDLLImpl.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
MYDLL_API void * CreateIMyDLL()
{
CMyDLLImpl * pMyDLLImpl = new CMyDLLImpl();
return static_cast <void *> (pMyDLLImpl);
}
这样基本上就写完了。调用端使用的时候只要DLL的头文件(MyDLL.h),库文件和接口文件(IMyDLL.h)
为什么要使用接口写DLL,一个原因也就是MSDN说的在某些时候如果你给调用端一个类的头文件,可能这个头文件需要include很多其他的文件,对调用端并不合适。
我自己也碰到过类似的问题,调用端不能编译,后来改成接口文件就好了。
使用接口文件,就没有其他的什么文件。另外一个原因就是接口设计自身的好处了,这里就不说了。
还要记得在接口文件中定义一个Release函数。供调用端释放内存
Release函数很简单,就是写
delete this;
经验分享,如何使用try,catch, throw之一
try,catch, throw的含义就很清楚,但怎么使用,我还是过了好多年才明白一些
先看一个不好的例子
//----------------- 不好的代码 -----------------
//----------------- 不好的代码 -----------------
class CTest1;
class CTest2;
class CTest3;
void BadCode()
{
//new test1
CTest1 * pTest1 = new CTest1;
//do something
bool bRet = DoSomething();
if (!bRet)
{
delete pTest1;
return;
}
//new CTest2
CTest2 * pTest2 = new CTest2;
//do something
bRet = DoSomething();
if (!bRet)
{
delete pTest1;
delete pTest2;
return;
}
//new CTest3
CTest3 * pTest3 = new CTest3;
//do something
bRet = DoSomething();
if (!bRet)
{
delete pTest1;
delete pTest2;
delete pTest3;
return;
}
//release
delete pTest1;
delete pTest2;
delete pTest3;
}
下面是我个人比较喜欢的写法
//--------- 好的例子 ---------------
//--------- 好的例子 ---------------
class CTest1;
class CTest2;
class CTest3;
void MyCode()
{
//define
CTest1 * pTest1 = NULL;
CTest2 * pTest2 = NULL;
CTest3 * pTest3 = NULL;
//使用try, catch, throw
try
{
//new test1
pTest1 = new CTest1;
//do something
bool bRet = DoSomething();
if (!bRet)
throw -1;
//new CTest2
pTest2 = new CTest2;
//do something
bRet = DoSomething();
if (!bRet)
throw -2;
//new CTest3
pTest3 = new CTest3;
bRet = DoSomething();
//do something
if (!bRet)
throw -3;
//release
delete pTest1;
pTest1 = NULL;
delete pTest2;
pTest2 = NULL;
delete pTest3;
pTest3 = NULL;
}
catch()
{
if (pTest1)
delete pTest1;
if (pTest2)
delete pTest2;
if (pTest3)
delete pTest3;
}
}
另外在一个函数里不仅仅只有一个try,catch。可以有多个。比如:
//--------- 多个try的例子 ---------------
class CTest1;
class CTest2;
class CTest3;
void MyCode()
{
//define
CTest1 * pTest1 = NULL;
CTest2 * pTest2 = NULL;
CTest3 * pTest3 = NULL;
//使用try, catch, throw
try
{
//new test1
pTest1 = new CTest1;
//do something
bool bRet = DoSomething();
if (!bRet)
throw -1;
}
catch()
{
;
}
if (pTest1)
delete pTest1;
try
{
//new CTest2
pTest2 = new CTest2;
//do something
bRet = DoSomething();
if (!bRet)
throw -2;
}
catch()
{
;
}
if (pTest2)
delete pTest2;
try
{
//new CTest3
pTest3 = new CTest3;
bRet = DoSomething();
//do something
if (!bRet)
throw -3;
}
catch()
{
;
}
if (pTest3)
delete pTest3;
}
简单说一下
第一种写法,需要在不同的地方delete 不同的变量
第二种写法,在catch里delete所有的变量,代码的结构看起来更容易读,也易于维护
//define
CTest1 * pTest1 = NULL;
CTest2 * pTest2 = NULL;
CTest3 * pTest3 = NULL;
变量定义一定要在try之前定义,否则catch里找不到这些变量的定义
另外很重要的一点,在delete之后必须将变量设成NULL
catch里的delete可以删掉
把try里的delete放catch后,因为指针都初始化NULL了
经验分享,如何使用try,catch, throw之二
什么时候使用try,catch,什么时候不用;什么时候用throw,什么时候不用。工作了很多年才明白。
我个人的理解是:
1。在private或者protected的成员函数不使用try,catch,而只使用throw
2。如果在private或者protected的成员函数需要使用try,catch,那么就要使用rethrow
3。在public成员函数里使用try,catch
4。如果该类相对于整个项目来说是属于被调用层,那么public成员函数也可以不使用try,catch
5。如果调用第三方的代码,我一般都会用try,catch
我个人的习惯是把private或者protected成员函数的名字使用前缀__,public函数不用
先看一个我不喜欢的写法
//------------- try, catch, throw 例子,不喜欢的写法 ------------
//------------- try, catch, throw 例子,不喜欢的写法 ------------
class CTest
{
public:
int Init();
private:
int __InitA();
int __InitB();
int __InitC();
}
//--------- Init ------------
int CTest:Init()
{
try
{
int err;
err = __InitA();
if (err != 1)
throw -1;
err = __InitB();
if (err != 1)
throw -2;
err = __InitC();
if (err != 1)
throw 3;
return 1;
}
catch(int & err)
{
return err;
}
}
//---------- __InitA ----------
int CTest::__InitA()
{
try
{
int err;
err = DoSomething1();
if (err != 1)
throw -1;
err = DoSomething2();
if (err != 1)
throw -2;
return 1;
}
catch(int & err)
{
return err;
}
}
__InitB, ___InitC和___InitA类似
下面是我个人比较喜欢的写法
C/C++ code
//------------- try, catch, throw 例子,喜欢的写法 ------------
class CTest
{
public:
int Init();
private:
int __InitA();
int __InitB();
int __InitC();
}
//--------- Init ------------
int CTest:Init()
{
try
{
__InitA();
__InitB();
__InitC();
return 1;
}
catch(int & err)
{
return err;
}
}
//---------- __InitA ----------
int CTest::__InitA()
{
int err;
err = DoSomething1();
if (err != 1)
throw -1;
err = DoSomething2();
if (err != 1)
throw -2;
return 1;
}
__InitB, ___InitC和___InitA类似
我喜欢的写法是private或者protected函数没有try,catch,只有throw
在public函数就不需要去判断每个private或者protected函数的返回值了
看起来会更清楚一点
如果在private或者protected的成员函数需要使用try,catch,那么就要再throw,而不是调用return
回想一下windows的win32函数,返回有2种方式
一种是返回一个错误值
另外一种就是抛出异常
在我自己的设计中,什么时候返回值什么时候抛出异常,很值得推敲一下
另外,仔细考虑一下private和protected函数,它们永远都会返回1或者在它们出错的时候就会throw,那在写函数声明的时候也就不需要返回值了,就返回void就可以了,也就不需要写return语句了。
然后,在某些情况下,有时候我们真的需要private或者protected函数返回错误代码,而不是throw,这个时候如果再声明函数的时候有返回值,就可以和没有返回值的函数区别开了。
经验分享,代码编写习惯
以下是我个人的习惯:
1。将所有的错误代码返回值写在一个文件里,使用define来定义错误代码
2。写几const来定义操作错误代码,比如:
typedef int MY_ERR;
const int MY_UNKNOW_ERR 0
const int MY_OK 1
#define MY_SUCCEEDED(Status) ((MY_ERR)(Status) == MY_OK)
#define MY_FAILED(Status) ((MY_ERR)(Status) != MY_OK)
3。函数的返回值基本上都是MY_ERR,或者是void,很少有BOOL。因为很多情况下我们需要知道函数错在哪里,错误代码是什么。
函数返回值是MY_ERR,而不是int。这样表示函数返回的是一个错误代码。如果函数返回int,那就表示函数的确是返回一个int的数值
4。如何写类函数,请参考我的帖子"经验分享,如何使用try,catch, throw之二"
5。基本上调用public函数的写法是:
MY_ERR err = myclass.test1();
if MY_FAILED(err)
throw err;
6。写一个错误消息映射文件,即每个错误代码映射一窜字符。字符可以是多语言的。一般写在xml文件里。这样根据每个函数的返回值,就可以得到映射的字符串。
7。另外会有一个专门写日志的文件,日志文件有两种,一个是给用户看的操作日志,一个是给自己看的调试日志
8。尽量不用在底层的代码里写MessageBox这样的函数。一个项目在哪里弹出对话框,大多少情况下都是由GUI来决定的,不是由底层的代码来决定的。
9。提供一个common或者叫public的类,提供所有项目都可以使用的函数或者类
10。另外就是总所周知的事情,使用N层系统去做代码设计
日志有两种,一种是操作日志,一种是调试日志
操作日志是记录用户每一步的操作情况,也不一定要写的很详细,也不一定要记录每一步
调试日志是你认为需要记录的内容,比如一些变量的值。
调试日志你可以写个工具实时查看,这样有一个好处,可以实时调试程序,
在一些情况下,我们不能用.NET的调试工具,我们需要实时运行程序,不能用断点中断调试,这时候用调试日志就很有用。
比如在调试线程的时候,有时候使用断点是不能得到正确结果的。
我个人的习惯是调试信息写成“【类名】【方法名】调试信息”
另外对于错误的信息,不仅要写到日志里,还要考虑是否弹出对话框给用户
错误代码的使用
先看一个不好的例子
先看一个不好的例子
C/C++ code
//------ 不好的例子 -------
void Test()
{
if (xxx1)
{
MessageBox(xxx);
return;
}
if (xxx2)
{
MessageBox(xxx);
return;
}
if (xxx3)
{
MessageBox(xxx);
return;
}
}
我个人喜欢的写法是:
void Test()
{
try
{
if (xxx1)
throw ERROR_CODE_1;
if (xxx2)
throw ERROR_CODE_2;
if (xxx3)
throw ERROR_CODE_3;
}
catch(ERROR_CODE & err)
{
TCHAR * tchMsg = GetMessage(err);
MessageBox(tchMes);
}
}
提供一个函数,通过error code得到错误信息
我在写下位机程序,并且下位机内存有限,个人认为要尽量使用#define,不知道对不对。
我在写下位机程序时就碰到内存不够的问题,将const改成#define就好了
另外在使用enum定义错误代码的时候,有时候需要从enum转换到int,自己感觉有点麻烦
所以自己也就使用宏定义了,不过是一定要小心使用
线程函数写成类的protected函数
如果这个函数不是属于类的,那么就不能访问类的私有成员变量
现在的前提是这个函数是类的函数,它就可以访问类的所有成员变量
换一个角度,你先设计好了一个类,后来发现这个类的其中一个方法在线程下运行更合适,这个时候就把这个函数设计成线程调用的函数
并且,这个函数仍然是类的一个成员函数,你不能因为它在线程下运行更合适,就更改你的类的设计
线程是操作系统的概念,不是类的概念
经验分享,C语言代码编写习惯
C++可以利用try, catch, throw来写代码风格
C语言没有, 我个人使用goto语句来写代码风格
大家都知道最好不要使用goto, 但不是完全不要使用goto语句
函数设计有一个原则, 就是一个函数只有一个出口, 也就是只有一个地方有return
Code
先看一个不好的例子
C/C++ code
//--------- 不好的例子 ----------
boolean fun1()
{
if (__funx1() != 0)
return false;
if (__funx2() != 0)
return false;
if (__funx3() != 0)
return false;
return true;
}
以下是我个人喜欢的风格
//--------- 我个人喜欢的风格 ----------
//定义一个全局变量
int g_nErrorCode = 0;
//在函数__funx1, __funx2, __funx3中如果出错, 会设置g_nErrorCode值
//这个函数会有两个出口, 一个return true; 一个return false;
boolean fun1()
{
if (__funx1() != 0)
goto error;
if (__funx2() != 0)
goto error;
if (__funx3() != 0)
goto error;
return true;
error:
return false;
}
//如果要这个函数只有一个出口, 可以按以下的代码去写
boolean fun1()
{
bool bRet = true;
if (__funx1() != 0)
goto error;
if (__funx2() != 0)
goto error;
if (__funx3() != 0)
goto error;
//这句话永远都不能成立, 只有goto语句能进入
if (false)
{
error:
bRet = false;
}
return bRet;
}
经验分享,串口的通讯结构代码
我在工作中写过串口调用的代码,我想我写的代码应该还算是比较规范,可以给大家做一个参考
在代码中会用到一个叫SAMA.dll的文件, 关于它的源代码可以去下载,下载地址是
http://d.download.csdn.net/down/1150752/zyx040404
我写的这个串口封装调用是用在探针台的自动上下片系统里, 串口的封装代码在SAMA里, 此串口的通讯代码的结构是:
ILoader.h接口文件, 定义了上位机和下位机发送命令的函数等等
LoaderImpl.h, LoaderImpl.cpp 继承了ILoader.h文件, 是实现体
LoaderMCUDataDefine.h定义了上下位机共同使用的数据结构和命令格式代码以及错误代码,上下位机的命令格式是第一个字节是0xFF,第二和第三个是重复的命令字节,这样可以避免收到错误的串口数据
LoaderDataDefine.h定义了上位机的一些数据结构和消息定义,这些消息将发送给主窗口,由主窗口做相应的处理
我现在写上部分代码
Code
//ILoader.h
C/C++ code
#pragma once
#include "../SAMA/SAMADataDefine.h"
#include "LoaderDataDefine.h"
#include "LoaderErrorCode.h"
struct ILoader
{
//relese
virtual inline void Release() = 0;
//Initialize
virtual COMMON_ERR Initialize(const TCHAR * tchConfigFile) = 0;
//送消息的窗口句柄
virtual inline void SetNotifyWindow(HWND hWnd) = 0;
//send command to low machine
virtual inline COMMON_ERR SendCommandToLowMachineEx(BYTE iCommand, int iValue, BOOL bDirection) = 0;
//send other command to low machine
virtual inline COMMON_ERR SendCommandToLowMachine(BYTE iCommand, DWORD dwWaitTime = LOADER_MIN_WAIT_LOW_MACHINE_RETURN_TIME, BOOL bIsForcedSend = FALSE, BOOL bWaitResult = FALSE) = 0;
//set comm infromation
virtual inline COMMON_ERR SetCommInformation() = 0;
//update low machine para
virtual inline COMMON_ERR GetLowMachinePara() = 0;
virtual inline COMMON_ERR UpdateLowMachinePara(const tagLoaderPara & stuLoaderPara) = 0;
virtual inline BOOL IsWorking() const = 0;
virtual inline BOOL IsWarning() const = 0;
virtual inline int GetErrorCode() const = 0;
virtual inline int GetErrorValue() const = 0;
virtual inline COMMON_ERR IsValid() const = 0;
};
//LoaderImpl.h
//LoaderImpl.h
#pragma once
#include "ILoader.h"
#include "../SAMA/comm/CComm.h"
class CLoaderImpl : public ILoader, public CAsynThreadComm
{
public:
CLoaderImpl(void);
virtual ~CLoaderImpl(void);
//relese
virtual inline void Release();
//Initialize
virtual COMMON_ERR Initialize(const TCHAR * tchConfigFile);
//送消息的窗口句柄
virtual inline void SetNotifyWindow(HWND hWnd);
//send command to low machine
virtual inline COMMON_ERR SendCommandToLowMachineEx(BYTE iCommand, int iValue, BOOL bDirection);
//send command to low machine
virtual inline COMMON_ERR SendCommandToLowMachine(BYTE iCommand, DWORD dwWaitTime = LOADER_MIN_WAIT_LOW_MACHINE_RETURN_TIME, BOOL bIsForcedSend = FALSE, BOOL bWaitResult = FALSE);
//set comm information
virtual inline COMMON_ERR SetCommInformation();
//update low machine para
virtual inline COMMON_ERR GetLowMachinePara();
virtual inline COMMON_ERR UpdateLowMachinePara(const tagLoaderPara & stuLoaderPara);
virtual inline BOOL IsWorking() const;
virtual inline BOOL IsWarning() const;
virtual inline int GetErrorCode() const;
virtual inline int GetErrorValue() const;
virtual inline COMMON_ERR IsValid() const;
private:
HANDLE m_hWaitResult;
BOOL m_bIsWorking; //是否正在工作
BOOL m_bIsWarning; //是否正在报警
int m_nErrorCode;
int m_nErrorValue;
tagLoaderPara m_stuLoaderPara;
inline COMMON_ERR __SendCommandToLowMachineOnForce(BYTE iCommand, DWORD dwWaitTime = LOADER_MIN_WAIT_LOW_MACHINE_RETURN_TIME, BOOL bWaitResult = FALSE);
protected:
virtual void __OnReceive();
};
//LoaderImpl.cpp
//LoaderImpl.cpp
#include "StdAfx.h"
#include ".\Loaderimpl.h"
#include "../SAMA/SAMA.h"
#include "LogDefine.h"
#include "../SAMA/SAMAXML/IXMLParser.h"
#include <process.h>
#include <math.h>
static CLoaderImpl * l_pLoader = NULL;
//------------------------------------------------------------------------------------------------------
// CLoaderImpl
//------------------------------------------------------------------------------------------------------
CLoaderImpl::CLoaderImpl(void)
{
m_hWaitResult = CreateEvent(NULL, FALSE, FALSE, NULL);
m_bIsWorking = FALSE;
m_bIsWarning = FALSE;
m_nErrorCode = LOADER_ERROR_NO_ERROR;
m_nErrorValue = 0;
memset(&m_stuLoaderPara, NULL, sizeof(m_stuLoaderPara));
}
//------------------------------------------------------------------------------------------------------
// ~CLoaderImpl
//------------------------------------------------------------------------------------------------------
CLoaderImpl::~CLoaderImpl(void)
{
CloseHandle(m_hWaitResult);
}
//------------------------------------------------------------------------------------------------------
// Release
//------------------------------------------------------------------------------------------------------
inline void CLoaderImpl::Release()
{
delete this;
}
//------------------------------------------------------------------------------------------------------
// __OnReceive
//------------------------------------------------------------------------------------------------------
void CLoaderImpl::__OnReceive()
{
static BYTE byteAryMsg[MCU_SEND_BUFFER_SIZE];
memset(byteAryMsg, NULL, sizeof(byteAryMsg));
//business process
COMMON_ERR err = COMMON_OK;
size_t iLen = 0;
BOOL bIsWorking = FALSE;
try
{
//read comm
iLen = Read((char *) byteAryMsg, 1);
if (0 == iLen)
throw ERROR_LOADER_CAN_NOT_READ_SERIEAL_DATA;
if (byteAryMsg[0] != LOADER_H2L_ACTION)
throw ERROR_LOADER_IT_IS_NOT_ACTION;
//read receive command
iLen = Read((char *) byteAryMsg, 2);
if (byteAryMsg[0] != byteAryMsg[1])
throw ERROR_LOADER_IT_IS_NOT_COMMAND;
}
catch ()
{
//LOADER_LOG_DEBUG3(_T("[__OnReceive] error code: %d, byteAryMsg[0]: %d, byteAryMsg[1]: %d"), err, byteAryMsg[0], byteAryMsg[1]);
return;
}
LOADER_LOG_INFO1(_T("收到:%d"), byteAryMsg[0]);
try
{
switch(byteAryMsg[0])
{
case LOADER_H2L_GET_ERROR_CODE:
iLen = Readex((char *) byteAryMsg, 6);
m_nErrorCode = byteAryMsg[0];
m_nErrorValue = byteAryMsg[1];
LOADER_LOG_INFO1(_T("错误: %d"), m_nErrorCode);
//need set it now before post message,
//or caller will receive message first, and get working status later, it's wrong
m_bIsWorking = FALSE;
if (m_nErrorCode != LOADER_ERROR_NO_ERROR)
{
m_bIsWarning = TRUE;
if (m_hNotifyWindow)
PostMessage(m_hNotifyWindow, WM_LOADER_WARNING, (WPARAM) m_nErrorCode, NULL);
}
break;
case LOADER_H2L_UPDATE_PARA:
//m_bIsGetLowMachinePara = TRUE;
//send result
if (m_hNotifyWindow)
PostMessage(m_hNotifyWindow, WM_LOADER_UPDATE_PARA, NULL, NULL);
break;
case LOADER_H2L_GET_PARA:
//read data number
iLen = Readex((char *) byteAryMsg, 2);
{
//read data
int i = byteAryMsg[0] * 256 + byteAryMsg[1];
iLen = Readex((char *) byteAryMsg, i);
i = 0;
m_stuLoaderPara.nWaitVacuumTime = byteAryMsg[i] * 256 + byteAryMsg[i + 1];
i = i + 2;
}
//need set it now before post message,
//or caller will receive message first, and get working status later, it's wrong
m_bIsWorking = FALSE;
//send result
if (m_hNotifyWindow)
PostMessage(m_hNotifyWindow, WM_LOADER_GET_PARA, (WPARAM) &m_stuLoaderPara, NULL);
default:
break;
}
}
catch ()
{
LOADER_LOG_DEBUG1(_T("[__OnReceive] unknow error: %d"), GetLastError());
}
m_bIsWorking = bIsWorking;
ClearReceiveBuffer();
SetEvent(m_hWaitResult);
//接着LoaderImpl.cpp
//接着LoaderImpl.cpp
//------------------------------------------------------------------------------------------------------
// Initialize
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::Initialize(const TCHAR * tchConfigFile)
{
LOADER_LOG_INFO(_T("上下片系统初始化"));
l_pLoader = this;
try
{
if (g_pILogger)
g_pILogger->SetLogWindowName(_T("Loader Log"));
//init comm port
SetThreadPriority(THREAD_PRIORITY_NORMAL);
//if (!Open(m_stuLoaderSerial.nPort, m_stuLoaderSerial.nBaudRate))
// throw ERROR_LOADER_CAN_NOT_OPEN_COMM_PORT;
LOADER_LOG_DEBUG(_T("[Initialize] 自动上下片初始化成功"));
return COMMON_OK;
}
catch (COMMON_ERR & err)
{
LOADER_LOG_DEBUG1(_T("[Initialize] error code is : %d"), err);
throw err;
}
}
//------------------------------------------------------------------------------------------------------
// SendCommandToLowMachineEx
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::SendCommandToLowMachineEx(BYTE iCommand, int iValue, BOOL bDirection)
{
COMMON_ERR err = IsValid();
if COMMON_FAILED(err)
return err;
LOADER_LOG_INFO1(_T("发送:%d"), iCommand);
BYTE byteAryMsg[12] = {0};
byteAryMsg[0] = LOADER_H2L_ACTION;
byteAryMsg[1] = byteAryMsg[2] = iCommand;
if (bDirection)
byteAryMsg[3] = 1;
else
byteAryMsg[3] = 0;
byteAryMsg[4] = int(iValue / 256);
byteAryMsg[5] = iValue % 256;
byteAryMsg[6] = 0;
byteAryMsg[7] = 0;
byteAryMsg[8] = byteAryMsg[3] ^ byteAryMsg[4] ^ byteAryMsg[5] ^ byteAryMsg[6] ^ byteAryMsg[7];
byteAryMsg[9] = byteAryMsg[10] = byteAryMsg[11] = LOADER_H2L_END;
Write((char *) byteAryMsg, 12);
return COMMON_OK;
}
//------------------------------------------------------------------------------------------------------
// __SendCommandToLowMachineOnForce
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::__SendCommandToLowMachineOnForce(BYTE iCommand, DWORD dwWaitTime, BOOL bWaitResult)
{
return SendCommandToLowMachine(iCommand, dwWaitTime, TRUE, bWaitResult);
}
//------------------------------------------------------------------------------------------------------
// SendCommandToLowMachine
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::SendCommandToLowMachine(BYTE iCommand, DWORD dwWaitTime, BOOL bIsForcedSend, BOOL bWaitResult)
{
if (!bIsForcedSend)
{
COMMON_ERR err = IsValid();
if COMMON_FAILED(err)
return err;
}
if (bWaitResult)
ResetEvent(m_hWaitResult);
m_bIsWorking = TRUE;
LOADER_LOG_INFO1(_T("发送:%d"), iCommand);
BYTE byteAryMsg[6] = {0};
byteAryMsg[0] = LOADER_H2L_ACTION;
byteAryMsg[1] = byteAryMsg[2] = iCommand;
byteAryMsg[3] = byteAryMsg[4] = byteAryMsg[5] = LOADER_H2L_END;
Write((char *) byteAryMsg, 6);
//wait
if (bWaitResult)
{
DWORD dwRet = WaitForSingleObject(m_hWaitResult, dwWaitTime);
if (WAIT_OBJECT_0 != dwRet)
{
m_nErrorCode = ERROR_LOADER_WAIT_OVER_TIME;
return m_nErrorCode;
}
}
else
Sleep(dwWaitTime);
return COMMON_OK;
}
//------------------------------------------------------------------------------------------------------
// GetErrorData
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::GetErrorData()
{
return __SendCommandToLowMachineOnForce(LOADER_H2L_GET_ERROR_CODE);
}
//------------------------------------------------------------------------------------------------------
// GetLowMachinePara
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::GetLowMachinePara()
{
return __SendCommandToLowMachineOnForce(LOADER_H2L_GET_PARA);
}
//------------------------------------------------------------------------------------------------------
// UpdateLowMachinePara
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::UpdateLowMachinePara(const tagLoaderPara & stuLoaderPara)
{
LOADER_LOG_INFO1(_T("发送:%d"), LOADER_H2L_UPDATE_PARA);
//m_bIsGetLowMachinePara = FALSE;
m_stuLoaderPara.nWaitVacuumTime = stuLoaderPara.nWaitVacuumTime;
BYTE byteAryMsg[1000] = {0};
byteAryMsg[0] = LOADER_H2L_ACTION;
byteAryMsg[1] = byteAryMsg[2] = LOADER_H2L_UPDATE_PARA;
int i = 3;
byteAryMsg[i] = m_stuLoaderPara.nWaitVacuumTime / 256;
byteAryMsg[i + 1] = m_stuLoaderPara.nWaitVacuumTime % 256;
i = i + 2;
byteAryMsg[i] = byteAryMsg[i + 1] = byteAryMsg[i + 2] = LOADER_H2L_END;
Write((char *) byteAryMsg, i + 3);
return COMMON_OK;
}
//------------------------------------------------------------------------------------------------------
// SetCommInformation
//------------------------------------------------------------------------------------------------------
COMMON_ERR CLoaderImpl::SetCommInformation()
{
ConfigDialog();
return COMMON_OK;
}
//------------------------------------------------------------------------------------------------------
// IsWorking
//------------------------------------------------------------------------------------------------------
inline BOOL CLoaderImpl::IsWorking() const
{
return m_bIsWorking;
}
//------------------------------------------------------------------------------------------------------
// IsWarning
//------------------------------------------------------------------------------------------------------
inline BOOL CLoaderImpl::IsWarning() const
{
return m_bIsWarning;
}
//------------------------------------------------------------------------------------------------------
// GetErrorCode
//------------------------------------------------------------------------------------------------------
inline int CLoaderImpl::GetErrorCode() const
{
return m_nErrorCode;
}
//------------------------------------------------------------------------------------------------------
// GetErrorValue
//------------------------------------------------------------------------------------------------------
inline int CLoaderImpl::GetErrorValue() const
{
return m_nErrorValue;
}
//------------------------------------------------------------------------------------------------------
// IsValid
//------------------------------------------------------------------------------------------------------
inline COMMON_ERR CLoaderImpl::IsValid() const
{
COMMON_ERR err = COMMON_OK;
if (m_bIsWorking)
{
LOADER_LOG_INFO(_T("正忙"));
err = ERROR_LOADER_IS_WORKING;
}
if (m_bIsWarning)
{
LOADER_LOG_INFO(_T("报警"));
err = GetErrorCode();
}
if COMMON_FAILED(err)
{
if (m_hNotifyWindow)
PostMessage(m_hNotifyWindow, WM_LOADER_WARNING, (WPARAM) err, NULL);
}
return err;
}
//------------------------------------------------------------------------------------------------------
// SetNotifyWindow
//------------------------------------------------------------------------------------------------------
inline void CLoaderImpl::SetNotifyWindow(HWND hWnd)
{
m_hNotifyWindow = hWnd;
}
//LoaderMCUDataDefine.h
//LoaderMCUDataDefine.h
#ifndef LOADER_DATA_DEFINE_H
#define LOADER_DATA_DEFINE_H
struct tagLoaderPara
{
int nWaitVacuumTime; //等待真空时间
};
//------------------------------------------------------------------------------------------------------
// high machine to low machine command
//------------------------------------------------------------------------------------------------------
#define LOADER_H2L_END 255 //end
//those commands need not para
#define LOADER_H2L_ACTION 255 //action
#define LOADER_H2L_GET_ERROR_CODE 40 //get error code
//those commands need para
#define LOADER_H2L_UPDATE_PARA 206 //update para
//------------------------------------------------------------------------------------------------------
// error code
//------------------------------------------------------------------------------------------------------
#define LOADER_ERROR_NO_ERROR 0 //没有错误
#define LOADER_ERROR_UNKNOW_ERROR 255 //未知错误
//LoaderDataDefine.h
//LoaderDataDefine.h
#pragma once
#include "../SAMA/SAMADataDefine.h"
#include "../LoaderMCU/LoaderMCUDataDefine.h"
#define ID_TAGNAME_LOADER_SERIAL _T("串口")
#define ID_TAGNAME_LOADER_SERIAL_PORT _T("端口")
#define ID_TAGNAME_LOADER_SERIAL_BAUD_RATE _T("波特率")
//message
#define WM_LOADER_WARNING WM_USER + 800
#define WM_LOADER_PARA_IS_NOT_RIGHT WM_USER + 801
#define WM_LOADER_GET_PARA WM_USER + 807
#define WM_LOADER_UPDATE_PARA WM_USER + 814
#define LOADER_DEFAULT_START_WAFER_INDEX -1
#define LOADER_MAX_WAIT_LOW_MACHINE_RETURN_TIME 60000 //20 seconds
#define LOADER_MIN_WAIT_LOW_MACHINE_RETURN_TIME 600 //0.2 seconds
//还有一个是LoaderErrorCode.h
//还有一个是LoaderErrorCode.h
#pragma once
//[1000-1990] Loader
const int ERROR_LOADER_CAN_NOT_OPEN_COMM_PORT = 1000; //打开串口错误
const int ERROR_LOADER_INIT_FAILED = 1001; //自动上下片初始化错误
const int ERROR_LOADER_IS_INVALED = 1002; //上下片不可用
const int ERROR_LOADER_IS_WORKING = 1003; //自动上下片正在工作
const int ERROR_LOADER_IS_WARNING = 1004; //自动上下片报警
const int ERROR_LOADER_WAIT_OVER_TIME = 1030; //等待串口超时
const int ERROR_LOADER_CAN_NOT_READ_SERIEAL_DATA = 1032; //没有读到串口数据
const int ERROR_LOADER_IT_IS_NOT_ACTION = 1033; //不是下位机命令头
const int ERROR_LOADER_IT_IS_NOT_COMMAND = 1034; //不是下位机命令
该项目会生成一个dll文件,供主程序去调用
有关写日志的代码请参考SAMA,在我的资源里有下载
http://download.csdn.net/source/1150752
解释一下
SendCommandToLowMachine
SendCommandToLowMachineEx是发送命令并且带一个参数
__SendCommandToLowMachineOnForce是直接发送,不需要检查是否可以发送的状态,上面两个需要检查
参数dwWaitTime是等待下位机回复的时间
参数bIsForcedSend是否不需要检测状态直接发送
参数bWaitResult是否需要等待下位机一定要回复信息,这里会有一个事件对象
UpdateLowMachinePara是发送多个参数给下位机
__OnReceive需要仔细看看,前面一段是判断是不是我们规定的命令格式,中间是命令的处理
如果需要通知主窗口,还会发消息给主窗口,注意m_bIsWorking的付值时间和条件