MFC学习之旅--第三天

各种概念

  SM前缀,就是SystemMetric的缩写
  SM_C*SCREEN和SM_C*FULLSCREEN,FULLSCREEN相对于SCREEN减少了一些高度,FULLSCREEN是针对于SCREEN的一些修正,在FULLSCREEN中居中比SCREEN居中更好看一点。
  SM_CXCURSOR,光标的宽度。SM_CXDOUBLECLK,一般双击,我们认为是在同一点双击,但在双击的时候光标位置有一点差距也是可以的,两次点击的位置不超过SM_CXDOUBLECLK个像素,我们认为都是在双击。SM_CXFOCUSBORDER,当鼠标放在按钮上,老式的会产生虚线,虚线距离按钮边框SM_CXFOCUSBORDER个像素。SM_CXHSCROLL,滚动栏宽度
  dimension:空间范围(更有立体感的范围)
  editControl控件的wantReturn为TRUE,回车就是换行,ctrl+回车就是发送,wantReturn为FALSE,ctrl+回车就是换行,回车就是发送。AUTOHSCROLL,当输入字符超出单行编辑控件宽度时,自动往右滚动。
  WM_SETCURSOR消息,当光标在窗口的不同控件之间来回切换产生此消息
  _countof(数组array),计算静态分配下数组中的元素数。返回数组中的元素个数,以大小表示。实现:

#define _countof(array) (sizeof(array) / sizeof(array[0]))

各种函数

  GetSystemMetrics功能:返回指定的系统度量或系统配置。请注意,GetSystemMetrics的返回值单位都为像素。函数原型:

int GetSystemMetrics(
  [in] int nIndex
);

  第一个参数:请注意,所有SM_CX*值都是宽度,所有SM_CY*值都是高度。还要注意,所有用于返回BOOL数据的设置都将TRUE表示为任何非零值,将FALSE表示为零值。
  SetTimer功能:创建一个定时器。函数原型:

UINT_PTR SetTimer(
  [in, optional] HWND      hWnd,
  [in]           UINT_PTR  nIDEvent,
  [in]           UINT      uElapse,
  [in, optional] TIMERPROC lpTimerFunc
);

  第一个参数:要与定时器关联的窗口句柄。此窗口必须由当前正在调用定时器的线程拥有。如果想替换定时器,hWnd传入NULL,然后传入待替换的定时器的
定时器ID
  第二个参数:非零的定时器ID。如果hWnd参数为NULL,并且nIDEvent与已经存在的定时器不匹配,则将忽略这个已经存在的定时器,并且新的定时器ID会被生成。如果hWnd参数不为NULL,并且hWnd指定的窗口已经有一个值为nIDEvent的计时器,则新的定时器会替换旧的定时器。当SetTimer替换定时器时,定时器被重置。因此,当前超时值过后将发送消息,但忽略之前设置的超时值。如果调用不是为了替换现有计时器,那么如果hWnd为空,nIDEvent应该为0。
  第三个参数:超时值,以毫秒为单位。uElapse小于USER_TIMER_MINIMUM,则被设置为USER_TIMER_MINIMUM;大于USER_TIMER_MAXIMUM,则被设置为USER_TIMER_MAXIMUM
  第四个参数:指向超时值过期时要通知的函数的指针。如果lpTimerFunc为NULL,系统会向应用程序队列发送WM_TIMER消息。消息MSG结构的hwnd成员包含hwnd参数的值。
  返回值:如果函数成功且hWnd参数为空,则返回值为标识新计时器的整数。应用程序可以将该值传递给KillTimer函数以销毁计时器。如果函数成功且hWnd参数不为空,则返回值为非零整数,应用程序可以将nIDEvent参数的值传递给KillTimer函数以销毁计时器。如果函数未能创建计时器,则返回值为零。
  注意,应用程序可以通过在窗口过程中包含case(WM_TIMER)语句或在创建计时器时指定TimerProc回调函数来处理WM_TIMER消息。指定TimerProc回调函数时,默认窗口过程在处理WM_TIMER时调用回调函数。因此,即使使用TimerProc而不是处理WM_TIMER,也需要在调用线程中调度消息。定时器标识符nIDEvent特定于相关窗口。不同窗口相同的定时器ID所代表的定时器是不同的。WM_TIMER消息的wParam参数包含nIDEvent参数的值。在使用SetTimer或其他与计时器相关的功能之前,建议通过SetUserObjectInformation函数将UOI_TIMERPROC_EXCEPTION_SUPPRESSION标志设置为false,否则应用程序可能会出现不可预测的行为,并容易受到安全漏洞的攻击

MFC组成和原理

  MFC的组成:a)必须有一个CWinApp的派生类;b)必须用派生类在全局定义一个对象theApp;c)在派生类中必须重写InitInstance虚函数;d)MFC程序就用CWinApp派生类中的InitInstance虚函数作为主函数
  MFC内部接管了三个消息映射函数,分别是WM_INITDIALOG(接管后的函数名为OnInitDialog),WM_COMMAND下的IDOK和IDCANCEL(接管后的函数名为OnOK和OnCancel),所以这三个不会出现在MFC中的消息映射列表中。它们都为虚函数回调,它们都被系统缺省处理了。

UNICODE与多字符集

  Unicode与多字符集:是两种文字编码方式。a)多字符集:各国文字的编码之间有重叠的编码(编码冲突),const char* p = "abc221@%#$%",在中国显示p="中国";b)Unicode:各国统一编码,const whar_t * p=L"abcd32$";c)wchar_t是双字节变量;d)Unicode字符串常量必须在前面加上一个大写的L;e)TCHAR类型是两种编码格式的自适应类型;Unicode的优点在于,无论在哪个国家的操作系统中,各国文字都无障碍的显示出来,全世界所有语言独立占用各自不同的编码 ;早期的多字符集编码,经常是在各国操作系统内出现别过文字乱码的罪魁祸首。
  TCHAR:自适应char,在多字符集编码项目和Unicode编码项目间自动转换。当VS项目设置为多字符集时,TCHAR被系统定义为char类型;当VS项目设置为Unicode时,TCHAR被系统定义为wchar_t类型。
  Unicode与多字符集字符串互相转换:a)API:WideCharToMultiByte()与MultiByteToWideChar。WideChar,宽字符。MultiByte,窄字符。WideCharToMultiByte功能:此函数把宽字符转换成指定的新的字符串,如ANSI,UTF8等,新字符串不必是多字节字符集。函数原型:

int WideCharToMultiByte(
  [in]            UINT                               CodePage,
  [in]            DWORD                              dwFlags,
  [in]            _In_NLS_string_(cchWideChar)LPCWCH lpWideCharStr,
  [in]            int                                cchWideChar,
  [out, optional] LPSTR                              lpMultiByteStr,
  [in]            int                                cbMultiByte,
  [in, optional]  LPCCH                              lpDefaultChar,
  [out, optional] LPBOOL                             lpUsedDefaultChar
);

  第一个参数CodePage:指定要转换成的字符集代码页,它可以是任何已经安装的或系统自带的字符集,你也可以使用如下所示代码页之一。
    CP_ACP  当前系统ANSI代码页,A意为Active,即当前系统正在用的代码页,当前系统使用的哪一国的文字。
    CP_MACCP  当前系统Macintosh代码页
    CP_OEMCP  当前系统OEM代码页,一种原始设备制造商硬件扫描码
    CP_SYMBOL  Symbol代码页,用于Windows2000及以后版本
    CP_THREAD_ACP  当前线程ACP代码页,用于Windows2000及以后版本
    CP_UTF7  UTF-7,设置此值时lpDefaultChar和lpUsedDefaultChar必须是NULL
    CP_UTF8  UTF-8,设置此值时lpDefaultChar和lpUsedDefaultChar必须是NULL
    我想最常用的就是CP_ACP和CP_UTF8了,前者将宽字符转换成ANSI,后者转换为UTF8
  第二个参数dwFlags:指定如何处理没有转换的字符,但不设此参数函数会运行的更快一些,一般设置0即可。可设的值如下所示:
    WC_NO_BEST_FIT_CHARS  把不能直接转换成相应多字节字符的Unicode字符转换成lpDefaultChar指定的默认字符。也就是说,如果把Unicode转换成多字节字符,然后再转换回来,你不一定得到相同的Unicode字符,因为这期间可能使用了默认字符。此选项可以单独使用,也可以和其他选项一起使用。
    WC_COMPOSITECHECK  把合成字符转换成预制的字符。它可以与后三个选项中的任何一个组合使用,如果没有与他们中的任何一个组合,则与选项WC_SEPCHARS相同
    WC_ERR_INVALID_CHARS  此选项会致使函数遇到无效字符时失败返回,并且GetLastError会返回错误码ERROR_NO_UNICODE_TRANSLATION。否则函数会自动丢弃非法字符。此选项只能用于UTF8
    WC_DISCARDNS  转换时丢弃不占空间的字符,与WC_COMPOSITECHECK一起使用
    WC_SEPCHARS  转换时产生单独的字符,此是默认转换选项,与WC_COMPOSITECHECK一起使用
    WC_DEFAULTCHAR  转换时使用默认字符代替例外的字符,(最常见的如' ?'),与WC_COMPOSITECHECK一起使用
  当指定WC_COMPOSITECHECK时,函数会将合成字符转换成预制字符。合成字符由一个基字符和一个不占空间的字符(如欧洲国家及汉语拼音的音标)组成,每一个都有不同的字符值。预置字符由一个用于表示基字符和不占空间的合成体的单一的字符值
  当指定WC_COMPOSITECHECK时,也可以使用上表列出的最后3个选项来定制预制字符的转换规则。这些选项决定了函数在遇到宽字符的合成字符没有对应的预制字符时的行为,他们与WC_COMPOSITECHECK一起使用,如果都没有指定,函数默认WC_SEPCHARS
  对于下列代码页,dwFlags必须为0,否则函数返回错误码ERROR_INVALID_FLAGS。50220 50221 50222 50225 50227 50229 52936 54936 57002到57011 65000(UTF7) 42(Symbol)。对于UTF8,dwFlags必须为0或WC_ERR_INVALID_CHARS,否则函数都将失败返回并设置错误码ERROR_INVALID_FLAGS
  第三个参数lpWideCharStr:待转换的宽字符串
  第四个参数cchWideChar:待转换宽字符串的长度,-1表示转换到字符串结尾
  第五个参数lpMultiByteStr:接收转换后输出新串的缓冲区
  第六个参数cbMultiByte:输出缓冲区大小,如果为0,lpMultiByteStr将被忽略,函数将返回所需缓冲区大小
  第七个参数lpDefaultChar:指向字符的指针,在指定编码里找不到相应字符时使用此字符作为默认字符代替。如果为NULL则使用系统默认字符。对于要求此参数为NULL的dwFlags情况,使用此参数,函数将失败返回并设置错误码ERROR_INVALID_PARAMETER。
  第八个参数lpUsedDefaultChar:开关变量指针,用以表明函数是否使用过默认字符。对于要求此参数为NULL的dwFlags而使用此参数,函数将失败返回并设置错误码ERROR_INVALID_PARAMETER。lpDefaultChar和lpUsedDefaultChar都设为NULL,函数会更快一些。
  返回值:如果函数成功,且cbMultiByte非0,返回写入lpMultiByteStr的字节数(包括字符串结尾的0 );cbMultiByte为0,则返回转换所需字节数。函数失败,返回0.
  注意:函数WideCharToMultiByte使用不当,会影响程序的安全,调用此函数会很容易导致内存泄漏,因为lpWideCharStr指向的输入缓冲区大小是宽字符数,而lpMultiByteStr指向的输出缓冲区大小是字节数。为了避免内存泄漏,应确保为输出缓冲区指定合适的大小。我的方法是先使cbMultiByte为0调用WideCharToMultiByte一次以获得所需缓冲区大小,为缓冲区分配空间,然后再次调用填充缓冲区。另外,从Unicode UTF16向非Unicode字符集转换可能会导致数据丢失,因为该字符集可能无法找到表示指定Unicode数据的字符。
  第二个是多字节字符到宽字符转换函数,函数原型如下:

int MultiByteToWideChar(
  [in]            UINT                              CodePage,
  [in]            DWORD                             dwFlags,
  [in]            _In_NLS_string_(cbMultiByte)LPCCH lpMultiByteStr,
  [in]            int                               cbMultiByte,
  [out, optional] LPWSTR                            lpWideCharStr,
  [in]            int                               cchWideChar
);

  功能:此函数把多字节字符串转换成宽字符串(Unicode),待转换的字符串并不一定是多字节的。
  此函数的参数,返回值及注意事项参见上面函数WideCharToMultiByte的说明,这里只对dwFlags做简单解释。
  dwFlags:指定是否转换成预制字符或合成的宽字符,对控制字符是否使用象形文字,以及怎样处理无效字符。
    MB_PRECOMPOSED  总是使用预制字符,即有单个预制字符时,就不会使用分解的基字符和不占空间字符。此为函数的默认选项,不能和MB_COMPOSITE合用
    MB_COMPOSITE  总是使用分解字符,即总是使用基字符+不占空间字符的方式
    MB_USEGLYPHCHARS  使用象形字符代替控制字符
    MB_ERR_INVALID_CHARS  设置此选项,函数遇到非法字符就失败并返回错误码ERROR_NO_UNICODE_TRANSLATION,否则丢弃非法字符
  对于下列代码页,dwFlags必须为0,否则函数返回错误码ERROR_INVALID_FLAGS。50220 50221 50222 50225 50227 50229 52936 54936 57002到57011 65000(UTF7) 42(Symbol)。对于UTF8,dwFlags必须为0或WC_ERR_INVALID_CHARS,否则函数都将失败返回并设置错误码ERROR_INVALID_FLAGS
  使用com组件中的_bstr_t类,这个类可以随时存入和取出两种字符串,对于非Unicode字符串和Unicode字符串都接受。一般在MFC工程下使用。而CString只能接受自适应字符串。

_bstr_t bstr="Hello,";
bstr+=L"中国";
char * p = bstr;   //转换为窄字符
wchar_t* p2 = bstr; //转换为宽字符
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容