1、匈牙利规则(写程序代码规则)一、程序风格:1、严格采用阶梯层次组织程序代码:各层次缩进的分格采用 VC 的缺省风格,即每层次缩进为 4 格,括号位于下一行。要求相匹配的大括号在同一列,对继行则要求再缩进 4 格。例如:void main()long lI; /循环变量long lSum;/用来记录和float fAvg;/用来求平均值/对数进行累加。for( lI=0;lIclass CcmTVector3dpublic:TYPE x,y,z;、对常量(包括错误的编码)命名,要求常量名用大写,常量名用英文表达其意思。如:#define CM_FILE_NOT_FOUND CMMAKEHR(0X
2、20B) 其中 CM 表示类别。、对 const 的变量要求在变量的命名规则前加入 c_,即:c_+变量命名规则;例如:const char* c_szFileName;2、 函数的命名规范:函数的命名应该尽量用英文表达出函数完成的功能。遵循动宾结构的命名法则,函数名中动词在前,并在命名前加入函数的前缀,函数名的长度不得少于 8 个字母。例如:long cmGetDeviceCount();3、函数参数规范:、 参数名称的命名参照变量命名规范。、 为了提高程序的运行效率,减少参数占用的堆栈,传递大结构的参数,一律采用指针或引用方式传递。、 为了便于其他程序员识别某个指针参数是入口参数还是出口参
3、数,同时便于编译器检查错误,应该在入口参数前加入 const 标志。如:cmCopyString(const char * c_szSource, char * szDest)4、引出函数规范:对于从动态库引出作为二次开发函数公开的函数,为了能与其他函数以及Windows 的函数区分,采用类别前缀+基本命名规则的方法命名。例如:在对动态库中引出的一个图象编辑的函数定义为 imgFunctionname(其中 img 为 image缩写)。现给出三种库的命名前缀:、 对通用函数库,采用 cm 为前缀。、 对三维函数库,采用 vr 为前缀。、 对图象函数库,采用 img 为前缀。对宏定义,结果代码
4、用同样的前缀。5、文件名(包括动态库、组件、控件、工程文件等)的命名规范:文件名的命名要求表达出文件的内容,要求文件名的长度不得少于 5 个字母,严禁使用象 file1,myfile 之类的文件名。三、注释规范:1、函数头的注释对于函数,应该从“功能”,“参数”,“返回值”、“主要思路”、“调用方法”、“日期”六个方面用如下格式注释:/程序说明开始/=/ 功能: 从一个 String 中删除另一个 String。/ 参数: strByDelete,strToDelete/ (入口) strByDelete: 被删除的字符串(原来的字符串)/ (出口) strToDelete: 要从上个字符串中
5、删除的字符串。/ 返回: 找到并删除返回 1,否则返回 0。(对返回值有错误编码的要/ 求列出错误编码)。/ 主要思路:本算法主要采用循环比较的方法来从 strByDelete 中找到/ 与 strToDelete 相匹配的字符串,对多匹配 strByDelete/ 中有多个 strToDelete 子串)的情况没有处理。请参阅:/ 书名/ 调用方法:/ 日期:起始日期,如:2000/8/21.9:40-2000/8/23.21:45/=/函数名()/程序说明结束、 对于某些函数,其部分参数为传入值,而部分参数为传出值,所以对参数要详细说明该参数是入口参数,还是出口参数,对于某些意义不明确的参
6、数还要做详细说明(例如:以角度作为参数时,要说明该角度参数是以弧度(PI),还是以度为单位),对既是入口又是出口的变量应该在入口和出口处同时标明。等等。、 函数的注释应该放置在函数的头文件中,在实现文件中的该函数的实现部分应该同时放置该注释。、 在注释中应该详细说明函数的主要实现思路、特别要注明自己的一些想法,如果有必要则应该写明对想法产生的来由。对一些模仿的函数应该注释上函数的出处。、 在注释中详细注明函数的适当调用方法,对于返回值的处理方法等。在注释中要强调调用时的危险方面,可能出错的地方。、 对日期的注释要求记录从开始写函数到结束函数的测试之间的日期。、 对函数注释开始到函数命名之间应该
7、有一组用来标识的特殊字符串。如果算法比较复杂,或算法中的变量定义与位置有关,则要求对变量的定义进行图解。对难以理解的算法能图解尽量图解。2、变量的注释:对于变量的注释紧跟在变量的后面说明变量的作用。原则上对于每个变量应该注释,但对于意义非常明显的变量,如:i,j 等循环变量可以不注释。例如: long lLineCount /线的根数。3、文件的注释:文件应该在文件开头加入以下注释:/ 工程: 文件所在的项目名。/ 作者:*,修改者:*/ 描述:说明文件的功能。/ 主要函数:/ 版本: 说明文件的版本,完成日期。/ 修改: 说明对文件的修改内容、修改原因以及修改日期。/ 参考文献: /为了头文
8、件被重复包含要求对头文件进行定义如下:#ifndef _FILENAME_H_#define _FILENAME_H_其中 FILENAME 为头文件的名字。4、其他注释:在函数内我们不需要注释每一行语句。但必须在各功能模块的每一主要部分之前添加块注释,注释每一组语句,在循环、流程的各分支等,尽可能多加以注释。其中的循环、条件、选择等位置必须注释。对于前后顺序不能颠倒的情况,建议在注释中增加序号。例如:/1、注释for ()if()/注释else/注释/注释switch()case: / 注释case: / 注释default: /注释在其他顺序执行的程序中,每隔 3-5 行语句,必须加一个注
9、释,注明这一段语句所组成的小模块的作用。对于自己的一些比较独特的思想要求在注释中标明。四、程序健壮性:1、函数的返回值规范:对于函数的返回位置,尽量保持单一性,即一个函数尽量做到只有一个返回位置。(单入口单出口)。要求大家统一函数的返回值,所有的函数的返回值都将以编码的方式返回。例如编码定义如下:#define CM_POINT_IS_NULL CMMAKEHR(0X200):建议函数实现如下:long 函数名(参数,)long lResult; /保持错误号lResult=CM_OK;/如果参数有错误则返回错误号if(参数=NULL)lResult=CM_POINT_IS_NULL;goto
10、 END;END:return lResult;2、关于 goto 的应用:对 goto 语句的应用,我们要求尽量少用 goto 语句。对一定要用的地方要求只能向后转移。3、资源变量的处理(资源变量是指消耗系统资源的变量):对资源变量一定赋初值。分配的资源在用完后必须马上释放,并重新赋值。例:long * plAllocMem;/定义一个分配内存的变量。plAllocMem=(long*)calloc(40, sizeof( long );/分配一段内存。/处理分配内存错误if(plAllocMem=NULL)lResult=CM_MEM_ALLOC_FAILED;goto END;使用内存/
11、释放资源变量,并重新赋值。if(pAllocMem!=NULL)free(plAllocMem);pAllocMem=NULL;4、对复杂的条件判断,为了程序的可读性,应该尽量使用括号。例:if(szFileName!=NULL)goto: END;while(wSize- 0)*pvToMem+=pvFromMem+;END:return lResult;采用判断可以检查传入的指针错误,但是这样的判断是程序最终的编译代码变大,同时降低了最终发布的程序的执行效率。由于传入空指针明显是调用这函数的程序的错误,而不是这个函数的错误,我们可以考虑采用断言来代替指针检查,即用ASSERT( pvToM
12、em!=NULLgoto: END;这样只会在 debug 版中才会产生检查代码,而在正式发布版中不会带有这些代码。并且可以方便我们在程序调试中和测试时发现错误,同时又不影响程序的效率。在下面的一些情况中必须加断言:a、 函数的参数,特别是指针参数必须利用断言来进行确认。b、 利用断言检查程序中的各种假设的正确性。c、 在程序设计中不要轻易认为某种情况不可能发生,对你认为不可能发生的情况必须用断言来证实。为了使程序中的断言发挥作用,所有用于在开发内部进行测试或调试的动态库、执行程序、组件必须采用 debug 版。说明:在程序效率要求较高、或者调用比较频繁的函数,对入口参数的错误检查,使用断言方
13、式,其优点如上所叙,但其健壮性不强,所以在其他情况下,仍要求使用传统的检查方式,以增强程序的健壮性,当然,为了调试方便,可同时使用断言方式。、 严格的测试:对每一段代码都要求进行严格的测试,特别对一些功能函数要对其各种临界点(比如零值、无穷大的值等)进行测试。尽量做到每一段代码零错误。六、模块化规范:为了提高软件的重用性,减少重复开发的工作量。同时也为了提高程序的可读性,方便程序的维护,必须加强软件的模块化工作。模块化应该遵循以下几个基本规范:1、 个函数应该作到精而小,函数的代码应该控制在一个适度的规模,每个函数的代码一般不能超过 150 行,如果超过这个规模,应该进行模块化的工作。对于一些
14、特殊的函数确实要超过 150 行,应该提交出来讨论,通过后,要求编写者更加详细的对函数注释,并写明函数超行的原因,以及设计思想等。2、 某一功能,如果重复实现三遍以上,既应该考虑模块化,将其写成通用函数。并向开发人员发布。并要求将接口文档和实现的功能备案。3、 每一个开发人员要尽可能的利用其他人的现成的模块,减少重复开发。4、 对函数进行模块化时,要考虑函数的层次关系,特别是在增加新的功能模块时,对原来的函数代码要进行认真的调整,做到相同功能的不同函数没有重复代码,此要求的目的在于便于代码维护。举例如下:现有如下函数:/从 szFileName 文件中取 long cmGetSomething
15、(const char * c_szFileName,)CFile * pFile;/用来保存打开文件的地址pFile=new CFile(c_szFileName,CFile:modeRead);/用创建一个只读文件if(pFile=NULL)lResult=CM_POINT_IS_NULL;goto END;/从文件中读取/关闭文件delete pFile;END:return lResult;若现在需要增加如下接口的新函数:long cmReadSomething(CFile * pFile)if(pFile=NULL)lResult=CM_POINT_IS_NULL;goto END;
16、/从文件中读取END:return lResult;则要求如下:将 long cmGetSomething(const char * c_szFileName,)改为long cmGetSomething (const char * c_szFileName,)CFile * pFile; /用来保存打开文件的地址long lResult=CM_OK;/错误返回码/打开文件pFile=new CFile(c_szFileName,CFile:modeRead);if(pFile=NULL)lResult=CM_POINT_IS_NULL;goto END;/从文件中读取lResult=cmRe
17、adSomething(pFile,);IF_ERROR_GOTO_END/关闭文件delete pFile;END:return lResult;模块化的一些注意事项: 、设计好模块接口,用面向对象的观点看,包括:函数接口和变量接口。 、定义好接口以后不要轻易改动,并在模块开头(文件的开头或函数的开头)加以说明,所以在定义接口时,一定要反复琢磨,保持风格一致。 、注意全局变量也是一种接口,如果不是确实必要,应该尽量少用全局变量。 、在函数接口中,尽量使函数的接口容易理解和使用,其中每个输入输出参数都只代表一种类型数据,不要把错误值和其他专用值混在函数的其他输入输出参数中。 、争取编写出永远成
18、功的函数,使调用者不必进行相应的错误处理。此规范为试行版,解释权属于*软件公司技术委员会!员工在编写程序时请参阅 FuncTemplate.h 和 FuncTemplate.cppFuncTemplate.h/工程: FuncTemplate.h /描述: 用来处理对二叉树的一些算法,以及矩阵内存分配。 /版本: FuncTemplate 1.0 版。 /typedef struct _NODE /定义一个节点struct _NODE * pLeftChild; /节点的左孩子struct _NODE * pRightChild; /节点的右孩子 NODE;/=/ 功能: 用循环来实现二叉树的
19、左序遍历 / 参数: pNode /(入口) pNode: 二叉树的入口地址,即根节点的地址。 /(出口) 无。 / 返回: long 的函数返回码,如果返回值为 CM_OK,表成功遍历,返回 / CM_POINT_IS_NULL 表二叉树指针为空。 /=/long cmWalkTreeUseCycle(const NODE * pNode);/=/ 功能: 对矩阵进行分配内存 / 参数: wRowSize,wColSize,ppplMatrix /(入口) wRowSize:矩阵的行数; /(入口) wColSize:矩阵的列数; /(入口) ppplMatrix:要分配的矩阵的地址; /(
20、出口) ppplMatrix:分配的矩阵的地址; / 返回: long 的错误号,如果返回值为 CM_OK,表分配成功,返回 / CM_POINTER_IS_NOT_NULL 表矩阵的入口地址值不为空。 / CM_MEM_ALLOC_FAILED 系统的内存不足 /=/long cmInitMatrix(const size_t wRowSize,const size_t wColSize,long * ppplMatrix);FuncTemplate.cpp/工程: FuncTemplate.cpp /作者: * /修改者: *,* /描述: 用来处理对二叉树的一些算法,以及矩阵内存分配。
21、/主要函数:cmWalkTreeUseCycle,cmInitMatrix /版本: FuncTemplate 1.0 版。 /完成日期:2000-8-26 /修改日期: 2000-8-27,2001-12-21 /参考文献: 图形程序开发人员指南(机械工业出版社) /#define STRICT#include “stdio.h“#include “stdlib.h“#ifdef _cplusplusextern “C“ #endif /* _cplusplus */#include “makehresult.h“#include “memory.h“#include “functempla
22、te.h“#define CM_MEM_POINTER_IS_NULL CMEMAKEHR(0X100) /表示指针不为空的错误#define CM_MEM_POINTER_IS_NOT_NULL CMEMAKEHR(0X101) /表示指针不为空的错误#define MAX_PUSHED_NODES 100 /定义最大压栈数量/程序函数说明开始/=/ 功能: 用循环来实现二叉树的左序遍历 / 参数: cpNode /(入口) cpNode: 二叉树的入口地址,即根节点的地址。 /(出口) 无。 / 返回: long 的函数返回码,如果返回值为 CM_OK,表成功遍历,返回 / MS_POIN
23、T_IS_NULL 表二叉树指针为空。 / 调用方法:在调用此函数前必须先初始化二叉树 / 思路: 如果正在访问某节点,如果该节点有左分枝,就先访问左分枝。/ 接着访问该节点,如果该节点还有右分支。就再访问右分支。 / 访问左分枝时,在该处栈中做标记,当处理完左分枝就访问此 / 处。访问完每一个节点后,如果该节点没有右孩子,并且栈已经/ 为空,那么对该节点的访问就完成,代码对每一个节点都重复以/ 上操作。 / 参阅: 图形程序开发人员指南(机械工业出版社)Page:927 / 日期: 2000/8/26.9:40-2000/8/26.21:45 /=/图解:/ 根节点/ / / / / / 左
24、孩子 右孩子/ / / / / / / / 左孩子 右孩子左孩子 右孩子HRESULT cmWalkTreeUseCycle(const NODE * cpNode) /程序函数说明结束HRESULT lResult;/用来保存返回的错误号。NODE * pNodeStackMAX_PUSHED_NODES;/用来作为节点的堆栈。NODE * ppNodeStack;/用来指示节点堆栈的指针。lResult=CM_OK;/判断树是否为空。if(cpNode !=NULL)pNodeStack0=NULL; /设置堆栈为空。ppNodeStack=pNodeStack+1;for(; /如果当前
25、的节点有左孩子,对当前点压栈。/并把当前点移到左孩子,开始遍历左子树,/如此,直到找到没有左孩子的节点。while (cpNode-pLeftChild!=NULL)*ppNodeStack+=(NODE*)cpNode;cpNode=cpNode-pLeftChild;/我们现在处于没有左孩子的节点,所以访问/节点,如果有右子树,然后访问右子树。或/后入的节点。重复节点的出栈直到我们找到/有右子树的节点,或所有节点出栈for(; /访问节点(调用别的函数,没有实现)cmVisitNode(cpNode);/如果节点有右孩子,使该孩子成为当前节/点并开始遍历其子树,否则回朔访问节点/直到我们发
26、现一个有右子树的节点,或所/有的入栈点已经被访问。if(cpNode-pRightChild!=NULL)cpNode=cpNode-pRightChild;break;/出栈下一个节点,我们可以访问它,并判/断是否有右子树。if(cpNode=*(-ppNodeStack)=NULL)/栈为空并且当前节点没有右子树,完/成遍历。lResult =CM_OK;goto END;/如果指针为空则设置返回值信息为 MS_POINT_IS_NULL。lResult=CM_POINTER_IS_NULL;END:return lResult;/程序函数说明开始/=/ 功能: 对矩阵进行分配内存 / 参
27、数: cwRowSize,cwColSize,ppplMatrix /(入口) cwRowSize:矩阵的行数; / cwColSize:矩阵的列数; / ppplMatrix:要分配的矩阵的地址; /(出口) ppplMatrix:分配的矩阵的地址; / 返回: long 的错误号,如果返回值为 CM_OK,表分配成功,返回 / CM_POINTER_IS_NOT_NULL 表矩阵的入口地址值不为空。 / CM_MEM_ALLOC_FAILED 系统的内存不足 / 调用方法:在调用此函数前必须先对传入值赋空。对传入值作引用。 / 如:定义: long * pplMatrix;对 pplMat
28、rix 赋空,即: / pplMatrix=NULL; 调用为:cmInitMatrix(10,10, / 思路: 先对每一行分配内存,再对每一行对应的列分配内存。 / 参阅: 无 / 修改人:/ 日期: 2000/8/29.9:40-2000/8/29.16:45 /=HRESULT cmInitMatrix(const size_t cwRowSize,const size_t cwColSize,long * ppplMatrix)/程序函数说明结束HRESULT lResult; /存储返回值。lResult=CM_OK;WORD wI; /循环变量/要求对传入值进行初始化 NULL.
29、if(*ppplMatrix!=NULL)lResult=CM_POINTER_IS_NOT_NULL;goto END;/对矩阵的行进行分配内存*ppplMatrix=(long*)malloc(cwRowSize*sizeof(long*);/如果分配失败则返回。if(*ppplMatrix)=NULL)lResult=CM_MEM_ALLOC_FAILED;goto END;/对每一行所拥有的列数进行分配内存。for(wI=0;wIcwRowSize;wI+)*ppplMatrix=(long*)malloc(cwColSize* sizeof(long);/如果分配失败则返回错误。if
30、(*ppplMatrix=NULL)lResult=CM_MEM_ALLOC_FAILED;goto END;/对内存置为零memset(*ppplMatrix,0,cwColSize*sizeof(long);END:/对错误进行处理。if(FAILED(lResult)/如果分配不成功则释放内存。if(*ppplMatrix!=NULL)/对每一行内存进行释放。for(wI=0;wIcwRowSize;wI+)if(*ppplMatrix!=NULL)free(*ppplMatrix);*ppplMatrix=NULL;/对列的内存进行释放。free(*ppplMatrix);*ppplM
31、atrix=NULL;return lResult;#ifdef _cplusplus#endif /* _cplusplus */错误处理扩展:makehresult.h/作者: */描述: 用来对返回代码的统一规定/主要函数:/参考文献:COM 技术内幕(微软组件对象模型);美Dale Rogerson 著/#ifndef _ERRORDEFINE_H_#define _ERRORDEFINE_H_#include “winerror.h“/图解/ 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1/ 1 0 9 8 7 6 5 4 3 2 1 0 9
32、8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0/ +-+-+-+-+-+-+/ |Sev|C|R| 设备代码 | 类编码 | 返回代码 |/ +-+-+-+-+-+-+/=/对设备代码的定义/对通用函数定义的设备代码#define FACILITY_CM 0x80/对虚拟现实函数定义的设备代码#define FACILITY_VR 0x81/对图象函数定义的设备代码#define FACILITY_IMG 0x82/=/定义类编码(后 16 位中的前 6 位)/从 0X000X3F/其中 0X380X3F 为给程序员保留的临时类编码。/标准类编码从 0X000X37
33、#define MEMORY_CLASS 0x01#define FILE_CLASS 0x02/*#define TEMP_CLASS1 0X38#define TEMP_CLASS2 0X39#define TEMP_CLASS3 0X3A#define TEMP_CLASS4 0X3B#define TEMP_CLASS5 0X3C#define TEMP_CLASS6 0X3D#define TEMP_CLASS7 0X3E#define TEMP_CLASS8 0X3F*/ /=/制作临时资源时位的前 3 位屏蔽(即为保存 8 个临时类),临时资源从0X00000X1FFF(程序员输
34、入值)/实际为 0XE0000XFFFF(宏转换后)#define ADDTEMPCLASS(lResult) (0X3810)|lResult)/定义返回通用函数的返回代码类型/对 lResult 的传入值在 0X00000X1FFF 之间#ifndef CMEMAKEHR#define CMSMAKEHR(lResult) MAKE_HRESULT(SEVERITY_SUCCESS,FACILITY_CM,ADDTEMPCLASS(lResult)#define CMEMAKEHR(lResult) MAKE_HRESULT(SEVERITY_ERROR,FACILITY_CM,ADDTE
35、MPCLASS(lResult)#endif /CMEMAKEHR/定义返回虚拟现实函数的返回代码类型/对 lResult 的传入值在 0X00000X1FFF 之间#ifndef VREMAKEHR#define VRSMAKEHR(lResult) MAKE_HRESULT(SEVERITY_SUCCESS,FACILITY_VR,ADDTEMPCLASS(lResult)#define VREMAKEHR(lResult) MAKE_HRESULT(SEVERITY_ERROR,FACILITY_VR,ADDTEMPCLASS(lResult)#endif /VREMAKEHR/定义返回
36、图象函数的返回代码类型/对 lResult 的传入值在 0X00000X1FFF 之间#ifndef IMGEMAKEHR#define IMGSMAKEHR(lResult) MAKE_HRESULT(SEVERITY_SUCCESS,FACILITY_IMG,ADDTEMPCLASS(lResult)#define IMGEMAKEHR(lResult) MAKE_HRESULT(SEVERITY_ERROR,FACILITY_IMG,ADDTEMPCLASS(lResult)#endif /IMGEMAKEHR/当发生返回代码时,转到 END 处/要求返回代码号用 lResult,标记号
37、用 END#define IF_ERROR_GOTO_END if(FAILED(lResult) goto END;/显示返回代码的描述信息#define DISPLAY_HRESULT_MESSAGE if(FAILED(lResult) cmDispResultMessage(lResult);typedef long HRESULT;typedef long LRESULT;/=/保持和 windows 系统(com)一致#define CM_OK S_OK#define CM_FALSE E_FAIL#define VR_OK S_OK#define VR_FALSE E_FAIL#
38、define IMG_OK S_OK#define IMG_FALSE E_FAIL#define CM_UNKNOW_ERROR CMEMAKEHR(0X000) /未知的错误/内存错误#define CM_MEM_ALLOC_FAIL CMEMAKEHR(0X001) /内存分配失败#define CM_MEM_FREE_FAIL CMEMAKEHR(0X002) /内存释放失败#define CM_INVALID_POINTER CMEMAKEHR(0X003) /无效的指针/文件操作#define CM_CREATE_FILE_FAIL CMEMAKEHR(0X010) /文件创建失败
39、#define CM_OPEN_FILE_FAIL CMEMAKEHR(0X011) /文件打开失败#define CM_CLOSE_FILE_FAIL CMEMAKEHR(0X012) /文件关闭失败#define CM_DELETE_FILE_FAIL CMEMAKEHR(0X013) /文件删除失败#define CM_FILE_HAS_EXISTED CMEMAKEHR(0X014) /文件已经存在#define CM_COPY_FILE_FAIL CMEMAKEHR(0X015) /文件拷贝失败#define CM_FILE_NOT_OPEN CMEMAKEHR(0X016) /文件
40、没有打开#define CM_READ_FILE_FAIL CMEMAKEHR(0X017) /文件读取失败#define CM_FIND_FILE_FAIL CMEMAKEHR(0X01 /文件查找失败#define CM_FILE_HAS_ERROR CMEMAKEHR(0X019) /文件本身有错#define CM_WRITE_FILE_FAIL CMEMAKEHR(0X020) /文件写入失败/数组操作#define CM_ARRAY_BEYOND CMEMAKEHR(0X060) /数组越界/函数操作#define CM_PARAM_BEYOND CMEMAKEHR(0X030)
41、/参数越界/数据库操作#define CM_OPEN_DATABASE_FAIL CMEMAKEHR(0X040) /数据库打开失败#define CM_OPEN_TABLE_FAIL CMEMAKEHR(0X041) /数据表打开失败#define CM_TABLE_BEYOND CMEMAKEHR(0X042) /数据表访问越界#define CM_CREATE_DSN_FAIL CMEMAKEHR(0X043) /创建数据源失败#define CM_TABLE_EXIST CMEMAKEHR(0X044) /表已经存在#define CM_DATABASE_NOT_OPEN CMEMAK
42、EHR(0X045) /数据库没有打开/其他操作#define CM_BEYOND_PARAM CMEMAKEHR(0x100) /参数越界#define CM_POINT_IS_INVALIAD CMEMAKEHR(0x101) /点为非法的点/图象操作#define CM_CREATE_BMP_FAIL CMEMAKEHR(0x200) /创建位图失败/线程操作#define CM_THREAD_IS_LOCK CMEMAKEHR(0x300) /线程背锁住/数学计算#define CM_EDGENUM_NOTENOUGH CMEMAKEHR(0x500) /边数不够/网络操作#define CM_