实践C++ 代码维护的思考
2016-01-25 16:30
886 查看
最初发表在QQ空间里,http://user.qzone.qq.com/31731705/blog/1309416291,正好有征文大赛,就放在这里吧。
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(263) | 评论(0) | 转发(0) |
0
上一篇:编写安全代码:数组和指针的本质以及何时不能互换
下一篇:我的C++技巧总结
相关热门文章
A sample .exrc file for vi e...
游标的特征
IBM System p5 服务器 HACMP ...
busybox的httpd使用CGI脚本(Bu...
Solaris PowerTOP 1.0 发布
linux dhcp peizhi roc
关于Unix文件的软链接
求教这个命令什么意思,我是新...
sed -e "/grep/d" 是什么意思...
谁能够帮我解决LINUX 2.6 10...
给主人留下些什么吧!~~
评论热议
[align=center][/align] 小问题有大智慧-代理服务器的监测 是几个月前的文章,最近碰到别人问如何设置代理的问题,又回顾了部分代码,虽然时间不长,还是有不少记不清了。 ,于是就整理了那个设置代理的函数,代码是实践的科学,每写一次,都会有点心得。 先把代码贴出来,重点的部分用粗体。这个函数的大概流程是,先查询当前的浏览器设置,然后根据用户的设定,再决定 1. 无代理 2. 使用自动配置脚本 3. 使用某个代理 这三个选项中的一个,根据不同的选项,设置具体的值,然后调用API设置代理选项。 void CWRSBar::ModifySetting( const Option::ProxyEntryInfo &pei ) { // refer to following value in WinInet.h // so as to use these values as index in array. // :) /* #define INTERNET_PER_CONN_FLAGS 1 #define INTERNET_PER_CONN_PROXY_SERVER 2 #define INTERNET_PER_CONN_PROXY_BYPASS 3 #define INTERNET_PER_CONN_AUTOCONFIG_URL 4 #define INTERNET_PER_CONN_AUTODISCOVERY_FLAGS 5 */ // 初始化数据结构,主要是Option数组 INTERNET_PER_CONN_OPTION_LIST List; INTERNET_PER_CONN_OPTION Option[6]; unsigned long nSize = sizeof(List); Option[0].dwOption = 0; Option[0].Value.dwValue = 0; // connection flags Option[INTERNET_PER_CONN_FLAGS].dwOption = INTERNET_PER_CONN_FLAGS; Option[INTERNET_PER_CONN_FLAGS].Value.dwValue = PROXY_TYPE_DIRECT; //|PROXY_TYPE_AUTO_DETECT; // proxy server Option[INTERNET_PER_CONN_PROXY_SERVER].dwOption = INTERNET_PER_CONN_PROXY_SERVER; Option[INTERNET_PER_CONN_PROXY_SERVER].Value.pszValue = NULL; // proxy bypass Option[INTERNET_PER_CONN_PROXY_BYPASS].dwOption = INTERNET_PER_CONN_PROXY_BYPASS; Option[INTERNET_PER_CONN_PROXY_BYPASS].Value.pszValue = NULL; // auto config URL Option[INTERNET_PER_CONN_AUTOCONFIG_URL].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL; Option[INTERNET_PER_CONN_AUTOCONFIG_URL].Value.pszValue = NULL; // others ... Option[INTERNET_PER_CONN_AUTODISCOVERY_FLAGS].dwOption = INTERNET_PER_CONN_AUTODISCOVERY_FLAGS; Option[INTERNET_PER_CONN_AUTODISCOVERY_FLAGS].Value.dwValue = AUTO_PROXY_FLAG_USER_SET | AUTO_PROXY_FLAG_DETECTION_RUN; List.dwSize = nSize; //sizeof(INTERNET_PER_CONN_OPTION_LIST); List.pszConnection = NULL; List.dwOptionCount = 5; List.dwOptionError = 0; List.pOptions = &Option[1]; // Use it like C macro class JustForOutputOption { public: void operator()( INTERNET_PER_CONN_OPTION *p ) { ATLASSERT( p ); INTERNET_PER_CONN_OPTION *Option = p; WRST( LOG_TREND_PROXY )( TEXT("Option[INTERNET_PER_CONN_FLAGS](0x%x): (0x%x), ") TEXT("Option[INTERNET_PER_CONN_PROXY_SERVER](0x%x): (%s), ") TEXT("Option[INTERNET_PER_CONN_PROXY_BYPASS](0x%x): (%s), ") TEXT("Option[INTERNET_PER_CONN_AUTOCONFIG_URL](0x%x): (%s), ") TEXT("Option[INTERNET_PER_CONN_AUTODISCOVERY_FLAGS](0x%x): (0x%x)."), Option[INTERNET_PER_CONN_FLAGS].dwOption, Option[INTERNET_PER_CONN_FLAGS].Value.dwValue, Option[INTERNET_PER_CONN_PROXY_SERVER].dwOption, Option[INTERNET_PER_CONN_PROXY_SERVER].Value.pszValue ? Option[INTERNET_PER_CONN_PROXY_SERVER].Value.pszValue : TEXT("NULL"), Option[INTERNET_PER_CONN_PROXY_BYPASS].dwOption, Option[INTERNET_PER_CONN_PROXY_BYPASS].Value.pszValue ? Option[INTERNET_PER_CONN_PROXY_BYPASS].Value.pszValue : TEXT("NULL"), Option[INTERNET_PER_CONN_AUTOCONFIG_URL].dwOption, Option[INTERNET_PER_CONN_AUTOCONFIG_URL].Value.pszValue ? Option[INTERNET_PER_CONN_AUTOCONFIG_URL].Value.pszValue : TEXT("NULL"), Option[INTERNET_PER_CONN_AUTODISCOVERY_FLAGS].dwOption, Option[INTERNET_PER_CONN_AUTODISCOVERY_FLAGS].Value.dwValue ); } }DebugOutputOption; // 查询当前的设置. if( InternetQueryOption( NULL, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, &nSize ) ) { DebugOutputOption( Option ); int proxyType = Option::MANUAL; std::tstring proxyAuto, proxyDirect; g_wrsCfg.GetOpt( WRS_TRENDPROXY_AUTO_NAME, proxyAuto ); g_wrsCfg.GetOpt( WRS_TRENDPROXY_DIRECT_NAME, proxyDirect ); if( pei.name == proxyAuto ) proxyType = Option::AUTO; if( pei.name == proxyDirect ) proxyType = Option::DIRECT; std::Bit32 flag; enum { USE_OLD_PAC = 1, USE_OLD_BYPASS, USE_OLD_PROXY, }; flag[USE_OLD_PAC] = flag[USE_OLD_PROXY] = flag[USE_OLD_BYPASS] = true; TCHAR proxy[BufSize], bypass[BufSize]; proxy[0] = bypass[0] = 0; // 根据用户的配置,做具体的设置 switch( proxyType ) { // 使用自动配置脚本,如果原来有值,使用原来的值。 case Option::AUTO: { Option[INTERNET_PER_CONN_FLAGS].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_AUTO_PROXY_URL; if( !Option[INTERNET_PER_CONN_AUTOCONFIG_URL].Value.pszValue || !lstrlen( Option[INTERNET_PER_CONN_AUTOCONFIG_URL].Value.pszValue ) ) { // use AUTO value now //Option[0].Value.pszValue = const_cast<LPTSTR>( pei.server.c_str() ); Option[INTERNET_PER_CONN_AUTOCONFIG_URL].Value.pszValue = const_cast<LPTSTR>( g_wrsTrendProxyCfg.autoURL.c_str() ); flag[USE_OLD_PAC] = false; } } break; // 直连 case Option::DIRECT: { Option[INTERNET_PER_CONN_FLAGS].Value.dwValue = PROXY_TYPE_DIRECT; } break; // 使用用户配置的代理 case Option::MANUAL: { Option[INTERNET_PER_CONN_FLAGS].Value.dwValue = PROXY_TYPE_DIRECT|PROXY_TYPE_PROXY; if( Option[INTERNET_PER_CONN_PROXY_SERVER].Value.pszValue ) GlobalFree( Option[INTERNET_PER_CONN_PROXY_SERVER].Value.pszValue ); _sntprintf( proxy, BufSize-1, TEXT("%s:%d"), pei.server.c_str(), pei.port ); ATLASSERT( lstrlen( proxy ) ); Option[INTERNET_PER_CONN_PROXY_SERVER].Value.pszValue = proxy; flag[USE_OLD_PROXY] = false; if( Option[INTERNET_PER_CONN_PROXY_BYPASS].Value.pszValue ) GlobalFree( Option[INTERNET_PER_CONN_PROXY_BYPASS].Value.pszValue ); lstrcat( bypass, g_wrsTrendProxyCfg.bypass.c_str() ); if( lstrlen( bypass ) ) lstrcat( bypass, TEXT(";") ); lstrcat( bypass, TEXT("<local>") ); Option[INTERNET_PER_CONN_PROXY_BYPASS].Value.pszValue = bypass; flag[USE_OLD_BYPASS] = false; } break; } DebugOutputOption( Option ); // 设置代理选项 InternetSetOption( NULL, INTERNET_OPTION_PER_CONNECTION_OPTION, &List, nSize ); // free memeory if( Option[INTERNET_PER_CONN_PROXY_BYPASS].Value.pszValue && flag[USE_OLD_BYPASS] ) GlobalFree( Option[INTERNET_PER_CONN_PROXY_BYPASS].Value.pszValue ); if( Option[INTERNET_PER_CONN_PROXY_SERVER].Value.pszValue && flag[USE_OLD_PROXY] ) GlobalFree( Option[INTERNET_PER_CONN_PROXY_SERVER].Value.pszValue ); if( Option[INTERNET_PER_CONN_AUTOCONFIG_URL].Value.pszValue && flag[USE_OLD_PAC] ) GlobalFree( Option[INTERNET_PER_CONN_AUTOCONFIG_URL].Value.pszValue ); // system // Notifies the system that the registry settings have been changed so that it verifies the settings on the next call to InternetConnect. // This is used by InternetSetOption. InternetSetOption( NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0 ); // ie // Causes the proxy data to be reread from the registry for a handle. No buffer is required. // This option can be used on the HINTERNET handle returned by InternetOpen. It is used by InternetSetOption. // so if we don't call this API ie might show previous value even after last API call. // //InternetSetOption( NULL, INTERNET_OPTION_REFRESH , NULL, 0 ); } } 这段代码中想特别说明的有2个地方, 1. 关于Option数组的使用,一开始的时候,是直接使用数字做为索引的,即Option[0],Option[1], Option[2], ..., 这样做当然可以工作。不过稍微隔几天,你就会发现记不清各个索引的函义了,哪个是放代理的,哪个是放URL的,那个是放bypass的等,特别不利于后面代码的维护。经过思考之后,还是觉得使用有意义的枚举名称更合适,看了WinInet.h定义的几个值,可以直接用做索引,于是将代码稍微修改,变成了上面的样子。Option[0]是占位用的,真正的有意义的Option是从Option[1]开始。这样一来,不管是在函数开头的初始化的部分,还是函数中间给个别选项的赋值,都显得特别清楚。Magic Number不见了。将这种方式和原来的方式做个简单的对比: a. 设计都在编码之前,在写代码之前,大都会有一个思路,Option[0]放什么,Option[1]放什么,可是由于使用数字做索引,代码类似这样 Option[1].dwOption = INTERNET_PER_CONN_FLAGS; Option[1].Value.dwValue = PROXY_TYPE_DIRECT; 或许开始时心里知道1代表着什么,但真正看代码,从代码的角度分析,却是反过来,是从右边的值来推导左边变量的含义的,这违反了编码的基本原则,有时使人困惑。即使Option[1]你在设计时并不打算放INTERNET_PER_CONN_FLAGS,从代码是看不出来的,相反,如果语句象下面这样, Option[INTERNET_PER_CONN_FLAGS].dwOption = INTERNET_PER_CONN_PROXY_SERVER; 你很快会发现其中的错误,因为左边右边不匹配。 b. a中开始良好的编码在后面也能体现出来优势,DebugOutputOption( Option ); 输出了Option数组的值,如果使用数字做索引,你还能清楚Option[0], Option[1]代表什么,它们的类型会是什么嘛?你需要惊人的记忆力。 使用有意义的枚举名做为索引,则可以轻松帮助你实现这个功能,事半功倍。你很容易就能确定哪个是DWORD,哪个是字符串。 Option[INTERNET_PER_CONN_FLAGS].Value.dwValue Option[INTERNET_PER_CONN_PROXY_BYPASS].Value.pszValue 2. JustForOutputOption是一个local class,真正的起作用的代码就一条语句,local class的定义见http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/topic/com.ibm.xlcpp8a.doc/language/ref/cplr062.htm,这里为什么要用Local Class呢?最初,在查询浏览器的代理设置之后和开始进行新的代理设置之前,就是直接打印调试信息的语句,当时就想虽然就是一句调试输出的代码,但这句代码比较复杂,而且重复了2次,有必要封装一下。不过,当时因为偷懒,并没有做。后来因为1的原因,Option数组中的顺序变了,不得不修改这2句调试语句,挺麻烦的,于是乎又有了包装的念头。首先想到的肯定是用一个独立的函数包装,想了一下放弃了,这个地方使用函数包装并不合适。 a. 如果使用一个单独的函数,函数的范围至少得是类的成员函数,但是却只在这个函数内部使用, b. 假设后面有其它函数会使用这个函数,函数的参数应该如何设计?传递Option类型的指针和数组大小嘛,那么Option数组的顺序呢?无法确保其它的函数的Option数组成员的顺序。 所以,从范围来看,作用域仅限于这个函数内部,没有必要影响整个类,甚至全局。从功能设计来看,也不适合设计函数,因为不通用。对熟悉C的开发者来说,在这个地方,宏是个选择,就用来作简单的文字替换即可。对于C++的开发者来说,Local class是个更好的选择,毕竟宏有许多缺点。因此,最终使用local class来包装这个调试输出语句,并且重载了operator(),使用起来象函数调用一样方便。 DebugOutputOption( Option ); Local class还有其它的用途,比如RAII等等。顺便提下,C++是一个备受争议的语言,很多其它语言的拥趸讥讽C++含有大量无用的特征,Local Class正是其中之一。 下这些人。 上面2点说得差不多了,最后补充一下,代码是写得玩的,未经QA测试,不排除有bug的可能性。 |
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(263) | 评论(0) | 转发(0) |
0
上一篇:编写安全代码:数组和指针的本质以及何时不能互换
下一篇:我的C++技巧总结
相关热门文章
A sample .exrc file for vi e...
游标的特征
IBM System p5 服务器 HACMP ...
busybox的httpd使用CGI脚本(Bu...
Solaris PowerTOP 1.0 发布
linux dhcp peizhi roc
关于Unix文件的软链接
求教这个命令什么意思,我是新...
sed -e "/grep/d" 是什么意思...
谁能够帮我解决LINUX 2.6 10...
给主人留下些什么吧!~~
评论热议
相关文章推荐
- 【转】C++ Vector 使用心得
- 【转】C++ ZLib压缩与解压缩
- 【转】C++ ZLib压缩与解压缩
- 【转】C++ Vector 使用心得
- 实践C++ 代码维护的思考
- 我的C++技巧总结
- 【转】c语言static和extern的用法
- 【转】c语言static和extern的用法
- 【转】C++ 内存池
- [原]Visual Studio 2008集成C++代码格式化插件
- C++开发工具的常用插件
- C++开发工具的常用插件
- [原]Visual Studio 2008集成C++代码格式化插件
- 【转】C++ 内存池
- 【转】C++的64位整数
- 【转】C++成员函数的内存分配问题
- 【转】C++类的实例分布
- 【转】C/C++字节对齐算法
- 【转】C/C++字节对齐算法
- 【转】C++类的实例分布