wenjiang 的个人资料morning's blog照片日志列表更多 工具 帮助

日志


6月30日

[转]在VC6.0中如何让new操作失败后抛出异常?

标准C 规定new一个对象时如果分配内存失败就应抛出一个std::bad_alloc异常,如果不希望抛出异常而仅仅传回一个NULL指针,可以用new的无异常版本:new(nothrow)。

字串7

VC6.0在<new>头文件中声明了这两种operator new操作符:

字串2

void *__cdecl operator new(size_t) _THROW1(std::bad_alloc);
void *__cdecl operator new(size_t, const std::nothrow_t&) _THROW0();

字串5

并分别定义在newop.cpp和newop2.cpp中。而_THROW0和_THROW1则是两个宏,在Include目录的xstddef文件中定义: 字串3

#define _THROW0() throw ()
#define _THROW1(x) throw (x)

字串6

newop.cpp和newop2.cpp对应的目标模块被打包进标准C 库中。标准C 库有若干个版本: libcp.lib(单线程静态版)、libcpd.lib(单线程静态调试版)、libcpmt.lib(多线程静态版)、libcpmtd.lib(多线程静态调试版)、msvcprt.lib(多线程动态版的导入库),msvcprtd.lib(多线程动态调试版的导入库),这些库与相应版本的C标准库一起使用,比如libcp.lib与libc.lib搭配。另外,VC6.0在new.cpp还定义了一个operator new,原型如下 :

字串8

void * operator new( unsigned int cb ) 字串8

而new.cpp对应的目标模块却是被打包进C标准库中的(是不是有点奇怪?)。 字串5

一般来说,程序员不会显式指定链接C 标准库,可是当程序中确实使用了标准C 库时链接器却能聪明地把相应的C 标准库文件加进输入文件列表,这是为什么?其实任何一个C 标准头文件都会直接或间接地包含use_ansi.h文件,打开它一看便什么都清楚了(源码之前,了无秘密) :

字串7

/***
*use_ansi.h - pragmas for ANSI Standard C libraries
*
* Copyright (c) 1996-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
* This header is intended to force the use of the appropriate ANSI
* Standard C libraries whenever it is included.
*
* [Public]
*
****/

字串4

#if _MSC_VER > 1000
#pragma once
#endif

字串5

#ifndef _USE_ANSI_CPP
#define _USE_ANSI_CPP

字串1

#ifdef _MT
#ifdef _DLL
#ifdef _DEBUG
#pragma comment(lib,"msvcprtd")
#else // _DEBUG
#pragma comment(lib,"msvcprt")
#endif // _DEBUG 字串2

#else // _DLL
#ifdef _DEBUG
#pragma comment(lib,"libcpmtd")
#else // _DEBUG
#pragma comment(lib,"libcpmt")
#endif // _DEBUG
#endif // _DLL 字串8

#else // _MT
#ifdef _DEBUG
#pragma comment(lib,"libcpd")
#else // _DEBUG
#pragma comment(lib,"libcp")
#endif // _DEBUG
#endif

字串5

#endif // _USE_ANSI_CPP

字串2

现在我们用实际代码来测试一下new会不会抛出异常,建一个test.cpp源文件:

字串2

// test.cpp
#include <new>
#include <iostream>
using namespace std;
class BigClass
{
public:
BigClass() {}
~BigClass(){}
char BigArray[0x7FFFFFFF];
};
int main()
{
try
{
BigClass *p = new BigClass;
}
catch( bad_alloc &a)
{
cout << "new BigClass, threw a bad_alloc exception" << endl;
}
BigClass *q = new(nothrow) BigClass;
if ( q == NULL )
cout << "new(nothrow) BigClass, returned a NULL pointer" << endl; 字串2

try
{
BigClass *r = new BigClass[1];
}
catch( bad_alloc &a)
{
cout << "new BigClass[1], threw a bad_alloc exception" << endl;
} 字串8

return 0;
}

字串6

根据VC6.0编译器与链接器的做法(请参考《为什么会出现LNK2005"符号已定义"的链接错误?》),链接器会首先在C 标准库中解析符号,然后才是C标准库,所以如果开发者没有自定义operator new的话最后程序链接的应该是C 标准库中newop.obj和newop2.obj模块里的代码。可是程序运行的结果却是: 字串5

new(nothrow) BigClass, returned a NULL pointer 字串2

显然程序始终未抛出bad_alloc异常。单步跟踪观察,发现第1个和第3个new实际上调用了new.cpp里的operator new,而第二个new(nothrow)则正确地调用了newop2.cpp定义的版本。很难理解是吧?但是当你用

字串6

dumpbin /SYMBOLS libcp.lib

字串7

dump出libcp.lib所有的符号信息时,你会发现其中的newop.obj模块没有定义任何符号(其它版本也一样)。不可思议!newop.cpp的实现代码明明写在那儿,怎么会....?让我们再仔细看看newop.cpp,咦,operator new的定义被包裹在一个#if...#endif块中:

字串1

#if !defined(_MSC_EXTENSIONS) 字串9

...
... 字串5

void *__cdecl operator new(size_t size) _THROW1(_STD bad_alloc)
{
...
...
} 字串8

#endif 字串5

哦,原来需要_MSC_EXTENSIONS宏未定义,实现代码才是有效的啊。那么这个宏是什么意思?其实Visual C 在语言层面上对ANSI C标准做了一些特殊的扩展,定义_MSC_EXTENSIONS意味着编译器支持这样的扩展,没有定义它编译器就会严格按照ANSI C标准来编译程序。实际上如果指定了编译选项/Ze编译器就会自动定义这个宏,指定/Za则不会,而且/Ze是缺省选项。作者猜想Visual Studio的开发人员在build标准C 库时很可能没有指定/Za,导致newop.cpp中的operator new定义被无情抛弃。是他们的疏漏吗?我看未必,大家可以试试用/Za选项去编译那些标准库文件,看看有多少编译不通过。VC标准库的实现用了很多微软扩展的语言特性,不指定/Za是情有可原的,我不明白的是newop.cpp的作者(好象是P.J. Plauger老人家)为什么会加上一个如此愚蠢的"#if !defined(_MSC_EXTENSIONS)",因为实在看不出这个operator new定义与_MSC_EXTENSIONS有什么冲突的地方。 字串5

既然标准C 库里的newop.obj是个空壳,那我们就只好自己动手丰衣足食了。把newop.cpp和dbgint.h(都在VC98\crt\src目录下)拷贝到test.cpp所在的目录,并将newop.cpp中的

字串3

#include <dbgint.h> 字串2

改成

字串8

#include "dbgint.h"

字串1

然后用 字串8

cl /c /Za /D_CRTBLD newop.cpp

字串2

编译它。/D_CRTBLD定义了_CRTBLD宏,为什么这么做呢?因为dbgint.h属于内部头文件,VC不希望应用程序用到它,便在文件中埋伏了这么一段: 字串7

#ifndef _CRTBLD
/*
* This is an internal C runtime header file. It is used when building
* the C runtimes only. It is not to be used as a public header file.
*/
#error ERROR: Use of C runtime library internal header file.
#endif /* _CRTBLD */ 字串2

可我们确确实实是想build标准库(的一部分),所以只好强行突破这个限制了。然后编译test.cpp: 字串9

cl /c /GX test.cpp

字串9

最后进行链接:

字串9

link test.obj newop.obj 字串2

这时再运行test.exe输出的结果就是

字串2

new BigClass, threw a bad_alloc exception
new(nothrow) BigClass, returned a NULL pointer
new BigClass[1], threw a bad_alloc exception 字串1

值得庆幸的是虽然VC6.0如此弱智,但VC7.1却表现良好,原因是VC7.1的newop.cpp和newaop.cpp(数组版)取消了那个愚的"#if !defined(_MSC_EXTENSIONS)",于是标准C 库中的newop.obj和newaop.obj模块都实实在在地有了相应代码。另外,nothrow版的定义也分别转移到了newopnt.cpp和newaopnt.cpp中。 字串8

后记: 作者在2001年便碰到过这个问题,百思不得其解,于是在CSDN论坛上发问,也不见答复。从此便搁置一旁,直到最近因探究LNK2005链接错误而彻底弄清楚VC链接器解析符号的规则后,才意识到二者或有联系。于是重拾旧疑,顺藤而上,果然问题就迎刃而解。此题虽小,功夫却做足,最后总算水落石出,解除了4年的积惑。 字串6

6月29日

比尔盖茨名言大搜集

“我应为王”

“公平不是总存在的,在生活学习的各个方面总有一些不能如意的地方。但只要适应它,并坚持到底,总能收到意想不到的成效。”

“在这个世界上,没有人能使你倒下。如果你自己的信念还站立的话。”

“轻率和疏忽所造成的祸患不相上下。有许多青年人之所以失败,就是败在做事轻率这一点上。”

“有非凡志向,才有非凡成就。”

“很多人喜欢拖延,他们对手头的事情不是做不好,而是不去做,这是最大的恶习。”

“一旦做出决定就不要拖延。任何事情想到就去做!立即行动!”

“好的习惯是一笔财富,一旦你拥有它,你就会受益终生。养成”立即行动“的习惯,你的人生将变得更有意义。”

“切实执行你的梦想,以便发挥它的价值,不管梦想有多好,除非真正身体力行,否则,永远没有收获。”

“成功开始于想法,但是,只有这样的想法,却没有付出行动,还是不可能成功的。”

“成功者一遇到问题就马上动手去解决。他们不花费时间去发愁,因为发愁不能解决任何问题,只会不断增加忧虑、浪费时间。”

“人们所认识到的是成功者往往经历了更多的失败,只是他们从失败中站起来并继续向前。”

“失败并非坏事,一次失败能教会你许多,甚至比你大学里所学的还有用。”

“破产是一种暂时的困境,贫困是一种思想的状态。”

“花费数百元买一本书,便可以获得别人的智慧经验。然而,如果你全盘模仿,不加思考,那有时就会画虎不成反类犬。”

“年轻人欠缺经验,但请不要忘记:年轻是你最大的本钱。不要怕犯错,也不要畏惧挑战,你应该坚持到底,在出人头地的过程中努力再努力。”

“获得成功有两个重要的前题:一是坚决,二是忍耐。”

“只要有坚强的持久心,一个庸俗平凡的人也会有成功的一天,否则即使是一个才识卓越的人,也只能遭遇失败的命运。”

“当你在事业上遇到挫折,有”打退堂鼓“的念头时,你应该加以注意,这是最危险的时候!”

“坚持下去,成功就在下一个街角处等着你。”

“机会并不会自动地转化为钞票——其中还必须有其他因素。简单地说,你必须能够看到它,然后必须相信你能抓住它。”

“强烈的欲望也是非常重要的。人需要有强大的动力才能在好的职业中获得成功。你必须在心中有非分之想,你必须尽力抓住那个机会。”

“企业发展需要的是机会,而机会对于有眼光的领导人来说,一次也就够了。”

“科学技术的进步将会给人们的生活带来巨大的影响,而人们要不断适应这种时代的变化,而不要坐等未来,失去自我发展的良好机会。”

“每一天都会有一个机遇,每一天都会有一个对某个人有用的机遇,每一天都会有一个前所未有的、绝不会再来的机会。”

“幸运之神会光顾世界上的每一个人,但如果她发现这个人并没有准备好要迎接她时,她就会从大门里走进来,然后从窗子里飞出去。”

“最有希望的成功者,并不是才华最出众的人,而是那些最善于利用每一时机发掘开拓的人。”

“一个人想要成功,就要学会在机遇从头顶上飞过时跳起来抓住它。这样逮到机遇的机会就会增大。”

“人生的选择决定一切”

“每项事业成功都离不开选择,而只有不同寻常的选择才会获取不同寻常的成功。”

“成功的轨迹作为一种策略路线,从一开始就应该走上正轨。”

 

“失败是成大事者之母。”

“这个世界并不在乎你的自尊,只在乎你做出来的成绩,然后再去强调你的感受。”

“他之所以为自己所领导的微软而感到自豪,是因为在这个团体中聚集了一大批与他一样热爱微软事业的人。”

“一个管理者如果不了解其下属的工作,那他就无法有效地管理他们。”

“对人才的运用,仅仅限于收罗是远远不够的,重要的是对人才不仅要善于识别其长处,而且要敢于大胆地使用,以让其充分显示自己的才能。”

“微软公司在用人上所表现出的胆略与气魄是别的公司无可比拟的。”

“对于一个大公司而言,没有一支强有力的服务队伍,给用户提供全面、周到的服务,那简直是难以想象的。”

“公司可以想出一些主意让员工自己寻找更好的办事方法,而绝不应该命令说”你必须选择这样的过程,你必须这么做“,这肯定行不通。”

“经过每一个里程碑式的重要阶段时,我们都力争做到没有任何瑕疵,就像做项目评估工作那样。”

“我们没有不懂技术的管理人员,因为,去寻求技术和管理之间的平衡毫不费力。”

“千万不要错过那些好小子,一旦发现必须下定决心,不然你会与他们失之交臂!”

“人生是不公平的,习惯接受吧。”

“你不会一离开学校就有百万年薪,你不会马上就是拥有公司配属手机的副总裁,二者你都必须靠努力赚来。”

“盖茨运用的管理风格既不是美国的个人主义式,也不是日本的共识主义式,而是独树一帜的达尔文式——适都生存”

“我工作是为了乐趣”

“微软公司雇用工作狂真是眼光独到。”

“每周经常工作72小时,有时甚至达到90小时;不工作的时候,他就像一个黑洞吸收光线那样,大量吸收信息。”

“没有热忱的经营者,也就教育不出敬业的员工。”

“如果你觉得你的老板很凶,等你做了老板就知道,老板是没有工作任期保障的。”

“要赞扬某人,最好用白纸黑字写下来;若要训斥某人,则要用电话的方式,不留痕迹。”

“在快餐店打工并不可耻,你的祖父对煎汉堡有不同的看法:机会。”

“由于缺乏团队合作而失败的工商企业,比由于其他原因而失败的要多。”

“如果你一事无成,不是你父母的错,所以不要对自己犯的错发牢骚,从错误中去学习。”

“要办好一个企业,固然必须摆平自上而下的利益关系,让处于企业内部各个层次的人,在发挥自己在企业中作用的同时,有一个相应的回报;但是建立良好的劳资关系,取得相互尊重,享受人与人之间的温暖和快乐同样是企业管理的大事。”

“看一下老板是不是善于管理他的员工,从他给员工支付的报酬毫无疑问地可以做出判断。”

“在学校里可能有赢家输家,在人生中却还言之过早。学校会不断给你机会找到正确的答案,真实人生中却完全不是这么回事。”

“在我们这里,体现员工地位和贡献,不是他的职务,而是他的业绩。他取得了成绩,大家都赞扬他,尊重他,以他为榜样,他就会有一种满足感。”

“我们需要的是世界上最优秀的人才!”

“人生没有寒署候,人生不是学期制,没有哪个雇主有兴趣帮你寻找自我,请用自己的时间来做这件事吧。”

 

“落后就是耻辱。”

“在计算机领域内,技术与应用发展更新极快,对其技术的掌握很难做到一劳永逸。有些人掌握了某种技能,生产出某种产品,就以为能一劳永逸,万事大吉了,这样非常危险的。”

“电视上演的并非真实人生。现实生活中每人都要离开咖啡馆去工作。”

“管理者在任何时候,任何情况下都有使员工们更加成熟的使命。”

“知道学习的重要性,知道该向谁学和学习什么,这正是比尔先生及其微软不断取得成功的重要经验。”“孜孜以求进步的精神,是一个人的优越的标记与胜利的征兆。”

“养成每天读十分钟书的习惯。这样每天十分钟,二十年之后,他的知识水平一定前后判若两人。只要他所读的都是好的东西。” “创新是做大公司唯一之路。”

“我很幸远,年纪轻轻就发现我的兴趣,而且令我如此着迷,至今仍是如此。”

“对书呆子好一点,你未来很可能就为其中一个工作。”

“失败是不可避免的,但只要坚持到底,总能收到意想不到的成效。”

“我们应该接受迅速失败,而不是缓缓失败,最不该接受的则是没有失败。如果有人从不犯错误,那只能说明他们努力不够。失败的结果是试图去尝试其它的可能。”

“巨大的成功靠的不是力量是韧性。社会竞争常常是持久力的竞争,有恒心和毅力的成功者往往成为笑到最后、笑得最好的人。”

“时间管理不仅是独乐,也是众乐的一场赛事,和时间赛跑,人人都有可能是胜利者。只有不参加的人,才是失败者。”

“我的工作其实是一场竞赛,我喜欢在事情到了紧要的关头时全力以赴的感觉。在这个时候,人往往有超水准的表现。”

“好的习惯主要是依赖于人的自我约束,或者说靠人对自我欲望的否定。”

“因为说话一定要诚实,所以一个好的领导者不能随意滥用奖赏和表扬,我会特别小心地对待我对员工所承诺的事情。”

“每天早晨醒来,一想到所从事的工作和所开发的技术将会给人类生活带来巨大的影响和变化,我就会无比的兴奋与激动。”

“成功都并没有什么秘密,他们只不过是适应了时代发展的变化。”

“当你的努力与时代同步时,你就会对社会产生不可忽略的影响。”

“可以说,我们对今后十年的主要见解是这样的:如果数字通讯是免费的,会出现什么情况呢?回答是,我们学习、采购、社交、做生意和娱乐的方式截然不同。我们希望软件和软件标准在其中起重大作用。”

“每隔三年左右,企业必须对自己业务的方方面面进行一次全方位的严格评估,这点至为关键。”

“当你了解客户的需求后,你必须乐于思考如何让产品更贴近并帮助客户。”

“对客户信守承诺,这一服务准则非常重要。”

“与其做一株绿洲中的小草,还不如做一棵秃丘中的橡树,因为小草毫无个性,而橡树昂首天穹。”

“科学技术的进步将会给人们的生活带来巨大的影响,而人们要不断地适应这种时代的变化,而不要坐等未来,失去自我发展的良好时机。”

“虽然行动不一定能带来令人满意的结果,但不采取行动就绝无满意的结果而言。”

“微软离破产只有18个月。他的意思是说,如果企业无法不断的创新进步,也许一年后就不复存在了。企业如此,人亦如此。”

“任何时候,人的脑子都会有很大一部分没有使用,因此,当你放眼四周时,就可以充分利用大脑。”

“最可怕的敌人,就是没有坚强的信念。”

“直觉助你发现职业,而选择职业就像盖房子,如果你选择的职业是坚实的河床,你会喜欢你的产品。”

“运气是一个因素,然而我想最重要的因素还是我们的远见和高度的洞察力。我从来都是戴着望远镜看这个世界的。”

“一个成功的商人,不应该仅仅是干练、能吃苦,还要有协调周围关系的能力,将不利因素化解到最小最少。做生意需要激情,但更要理智驾驭,意气用事、浮躁冲动是商家之大忌。什么是商人?商人就是关键时刻始终维护自己利益的人。”

“创办一个公司就像建立一座大厦,没有蓝图,就不可能顺利地施工,谁都不能在没有蓝图的情况下施工。建立事业的蓝图,就是订一份企业计划。”

“大多数的合伙人都采取五十对五十的分配法,这是最糟的方法,因为总得有人拥有做决策的能力才行。一旦公司开始赚钱,冲突必定随之产生,两倍合伙人意见必然相左,尤其是在问题牵涉到金钱时,双方争执愈烈。”

“你用于计划的时间越长,你完成工作所需要的时间就越短。”

“没有悟性的创业者,反应就不够灵敏,很难把自己的公司办得火起来。”

“增强团队精神是每位公司管理人必须做到的,只有强大的团队才能在市场的浪潮中立于不败之地,才能做大公司。没有强大的团队,新管理人的工作能力怎能得到下属的认可呢?”

“在你出生前,你的父母并不像现在这般无趣,他们变成这样是因为忙着付你的开销、洗你的衣服、听你吹嘘你有多了不起。所以在你拯救被父母这代人破坏的热带雨淋前先整理一下自己的房间吧。”

“优柔寡断是会传染的,它能使整个组织感染上这种病,引起人们犹豫不决,失去信心,甚至造成混乱。”

“了解我的公司在各区、各产品门类和各客户群体中的经营情况,对于我这个首席执行官来说是很重要的。”

“我认为做一个经营者有一个不可或缺的条件,那就是有经营兴趣。”

6月26日

看到一个不错的签名

既不回頭,何必不忘;既然無緣,何需誓言。今日種種,似水無痕,明夕何夕,君已陌路。

[转]运行期修改可执行文件的路径和Command Line

标 题: 【原创】运行期修改可执行文件的路径和Command Line
作 者: NetRoc
时 间: 2008-01-04,17:56
链 接: http://bbs.pediy.com/showthread.php?t=57682

    目前的很多主动防御工具和反XX系统,在对特定进程进行保护的时候,出于兼容性的考虑,都会保留一些白名单。特别是一些系统进程,例如csrss.exe、svchost.exe等等。而针对这些系统进程,判断是否在白名单中的方式,为了简便起见经常采用取系统路径、可执行文件名的方式。
    内核中比较明显的能够取到可执行文件路径的方法有下面几个:
1、  通过PEB. ProcessParameters -> ImagePathName取得可执行文件路径,通过PEB. ProcessParameters -> CommandLine取得执行的命令行,以及PEB. ProcessParameters里面其他几个成员取得其他一些相关的路径信息。
2、  通过nt!_EPROCESS的ImageFileName取得。
3、  通过nt!_EPROCESS:: SeAuditProcessCreationInfo:: ImageFileName取得。
4、  通过和_EPROCESS相关的文件对象信息取得。
    常见的方式一般只有1、2两种。而上述的前三种方式都可以在运行时被修改掉,用来进行欺骗。特别是PEB里面的信息由于在ring3直接就可以访问,实现上来说非常简单。
    下面这段代码通过NtQueryInformationProcess拿到PEB,然后修改路径信息:
HMODULE hMod = GetModuleHandle( _T( "ntdll.dll"));
  pfnNtQueryInformationProcess p = (pfnNtQueryInformationProcess)::GetProcAddress( hMod, "NtQueryInformationProcess");
  PROCESS_BASIC_INFORMATION stInfo = {0};
  DWORD dwRetnLen = 0;
  DWORD dw = p( GetCurrentProcess(), ProcessBasicInformation, &stInfo, sizeof(stInfo), &dwRetnLen);
  PPEB pPeb = stInfo.PebBaseAddress;
  WCHAR wszFullPath[MAX_PATH] = {0};
  WCHAR wszTmp2[MAX_PATH] = {0};
  wcscpy( wszFullPath, wszPath);
  MultiByteToWideChar( CP_THREAD_ACP, 0, szName, -1, wszTmp2, MAX_PATH);
  wcscat( wszFullPath, wszTmp2);
  wcscpy( pPeb->ProcessParameters->ImagePathName.Buffer, wszFullPath);
  pPeb->ProcessParameters->ImagePathName.Length = wcslen( wszFullPath) * sizeof(WCHAR);
  int nParamStart = 0;
  WCHAR *wszTmp = new WCHAR[pPeb->ProcessParameters->CommandLine.MaximumLength];
  ZeroMemory( wszTmp, sizeof(WCHAR) * pPeb->ProcessParameters->CommandLine.MaximumLength);
  wcscpy( wszTmp, pPeb->ProcessParameters->CommandLine.Buffer);
  if ( pPeb->ProcessParameters->CommandLine.Buffer[0] == '"')
  {
    for ( int i = 1; i < pPeb->ProcessParameters->CommandLine.Length / 2; i++)
    {
      if ( pPeb->ProcessParameters->CommandLine.Buffer[i] == '"')
      {
        nParamStart = i;
      }
    }
  }
  if ( nParamStart != 0)
  {
    if ( pPeb->ProcessParameters->CommandLine.Buffer[0] == '"')
    {
      pPeb->ProcessParameters->CommandLine.Buffer[0] = NULL;
      wcscat( pPeb->ProcessParameters->CommandLine.Buffer, L"\"");
    }
    else
    {
      pPeb->ProcessParameters->CommandLine.Buffer[0] = NULL;
    }
    wcscat( pPeb->ProcessParameters->CommandLine.Buffer, wszFullPath);
    wcscat( pPeb->ProcessParameters->CommandLine.Buffer, wszTmp + nParamStart);
  }
  delete[] wszTmp;
    这个方式可以欺骗通过toolhelp函数枚举出来的模块路径,以及直接读取PEB获取进程主模块路径的方式。
    另外,通过修改EPROCESS中的主模块名信息,可以欺骗一些在驱动层的程序。基本的代码如下:
    首先需要获取EPROCESS里面ImageFileName的偏移。这个函数必须在DriverEntry里面调用。
ULONG GetNameOffsetInEProcss()
{
  PEPROCESS pProcess = NULL;
  ULONG i = 0;
  pProcess = PsGetCurrentProcess();
  for ( i = 0; i < 0x1000; i++)
  {
    if ( strncmp( "System", (PUCHAR)pProcess + i, strlen("System")) == 0)
    {
      return i;
    }
  }
  return 0;
}
    然后可以在IoCtrl里面修改当前进程的名字:
case IOCTL_CHANGE_EXENAME:
      szName = (char*)Irp->AssociatedIrp.SystemBuffer;
      if ( !szName)
      {
        ntStatus = STATUS_UNSUCCESSFUL;
        break;
      }
      pEProcess = PsGetCurrentProcess();
      strncpy( (PCHAR)pEProcess + g_NameOffsetInEProcess, szName, 16);
      ntStatus = STATUS_SUCCESS;
      break;
    由于只是示例,所以只实现了良种方式。修改SeAuditProcessCreationInfo里面的信息,考虑到兼容性问题,可能稍微复杂一点。不过也可以比较容易的实现。
    这种方式的伪装可以穿过多少主动防御工具没有试过,大家可以去看看,哈哈。
    至于对付的方式,可以通过上面说的第四种取进程路径的方法。不过就比较复杂了,呵呵。
    流程就是,通过EPROCESS的SectionObject获得文件的FilePointer,通过ObQueryNameString取得这个对象的名字。然后就可以取得主映像模块的路径了。详细的代码可以参考wrk中NtQueryInformationProcess的相关实现。

6月25日

IT大腕眼中的比尔盖茨

  盖茨终将决定离开,告别他三十多年辛苦经营的事业——微软。

  盖茨将于本月27日正式退出微软的日常管理,将主要精力投入到慈善事业中去。盖茨表示他将把自己580亿美元财产,全数捐给名下慈善基金比尔及梅琳达盖茨基金会,不给儿女留下一分一毫。

  作为世界级的顶级人物,盖茨用他的一生创造了许多奇迹,并几乎引领了一个时代。盖茨多年的经验见证:"不论是经营他的软件帝国微软,还是在比尔及梅琳达盖茨基金会做慈善事业,他都一定会做到最好。"

  下面让我们一起看看,大腕眼中的盖茨是什么样子?

李开复:盖茨从好胜变慈祥

  谷歌全球副总裁、大中华区总裁李开复向谈及自己当年的老领导、微软即将退休的董事长比尔·盖茨,称他这些年完成了一个艰难并且伟大的转变,从一个争强好胜的年轻企业家成长为一个慈爱的人。

  说起自己眼中的盖茨,李开复归纳为“一个很率真、很谦虚的人”,然后补上一句“这在成功者之中相当少见。”

  李开复说:“盖茨除了微软要做好的产品,他还非常喜欢打败对手。他非常不高兴的是,微软落后竞争对手。所以如果微软在一个领域是第二名,他在追赶第一名的时候表现就非常厉害。”

1990年,春风得意的盖茨

  李开复回忆“在大约45岁的时候,盖茨发现人生的价值不是拥有多少,而是给后人留下多少。所以他捐出了财产,实现了个人的一次跨越。你知道,要让一个曾经那么喜欢争强好胜的人让出世界首富称号,是多么艰难,但他成功战胜了自己。”

唐骏:第一次看盖茨感觉是小孩 今天已是伟人

前盛大总裁、现新华都集团总裁兼CEO唐骏

  唐骏称,盖茨一度给外界孤僻的感觉,但在熟悉盖茨的人看来,他是一个非常可爱的人。唐骏说:“我和盖茨第一次接触是在1997年,感觉他调皮,有点小男孩的样子。不过,生活中可爱,不代表工作中可爱。在记忆中,工作状态的盖茨一度是一个锋芒毕露的人,总恨不得一步到位给别人指出问题。如今,他已变得慈祥了很多,成为一个长辈的形象。”

盖茨的可爱面

  唐骏说:“在微软期间,我觉得盖茨是一个天才,无论技术还是经营都是天才。盖茨创造的许可证销售模式奠定了微软今天的领袖地位。还有他对未来的预测,希望每个用户桌上都能有台电脑,当时都是了不起的预测,现在也都实现了。”

  盖茨是一个伟人,也是全世界的标杆。盖茨将全部财产都捐给慈善事业的举动,给包括我在内的很多人带来了对金钱、事业的思考。可能我现在还不理解他举动,但随着时间的流逝我想会慢慢理解,这也让我有很多想法。他已经成为一个楷模,是全世界的典范,并且是是神圣的典范。

  盖茨超脱了一般的企业家的境界,他认为自己是全世界的盖茨,是全世界赋予了他财富,要把这些财富还给全世界。通过这个方式回馈世界,回馈社会。

张亚勤:他对技术的热爱发自内心

微软中国有限公司董事长张亚勤

  亚勤说: “每个人进微软都可以选择一个“导师“,我2004年回到总部要求比尔做我的“导师“,他同意了。我现在也是三位微软人的“导师“。

  亚勤称“盖茨是一个技术的天才、商业的天才,也是一个十分幽默、善良的人。他是我碰到最聪明的人,技术功底相当深厚,另外他对商业的洞察力也很厉害,超过了我所碰到的所有人。他也是一个对技术有热情、对人类有使命感的人。盖茨有很多财富,但他自己的生活方式很简单,这种使命感是发自内心的,而不是装出来的。“

  亚勤称盖茨用30多年创立了微软公司,用创新的“技术”实现了自己的梦想,改变了世界。现在,他决定把所有的财富回馈给

社会,并投身慈善事业。我对他的退休感觉既留恋,又替他感到高兴,因为他又开始投入一项让他充满激情并且对整个社会都有益的事业当中去了。我十分的敬佩他。他的慈善事业我也愿意做一个志愿者,贡献一份力量。比尔对于财富的态度和他对梦想的不断追求,是我的楷模。

贝瑞特两次谈起盖茨:要像他一样热爱技术

图为英特尔全球董事长贝瑞特

  贝瑞特与成都电子科技大学学生交流的过程中,两次谈到比尔-盖茨。简单的言辞中,透露出一个巨人对另一个的认识与看法。

  贝瑞特说,“虽然比尔-盖茨没有经过正规的大学教育,但他在学校之外培养起了非常好的软件能力。而且当时是在发明第一台PC的时候就开始研究软件,他自己虽然还是一个学生,当时就跟一个优秀的工程师一样的,积累了很多实践的能力。”“比尔-盖茨没有拿到学位,但是深刻理解技术,深刻热爱着他所做的技术,知道自己该做什么”。他多次强调并重复要对技术有激情,要热爱自己所做的事情。 “要理解,如果你学习认真,真正掌握理解了这个技术,而且你热爱你所做的事情。你就可以为世界做很多神奇的事情,你可以改变世界,你可以成为下一个比尔-盖茨。一个人就可以做到这一点。”

总结

  盖茨他两次改变了世界:第一次是以软件推动了第三次工业革命;第二次,他用慈善拯救了无数生命,也改变了世人对财富的观念。对于他的退休,我们忠心的祝福他一路走好!

  附:视频比尔·盖茨在微软上班的最后一天

http://v.youku.com/v_show/id_XMzE1MzI2MjA=.html

突然发现

以前上学的时候,一起获得德智体全面发展的三好学生奖状的女生也不少,长大后怎么就遇不到德才貌兼备的女子呢?

Intel C++ Compiler 10.1

它可以与VC6完美结合工作,同时它的与VC7一样有 Zc:forScope 选项,解决VC6深恶痛绝的变量作用域问题.

 

不必修改工程,直接打开安装目录下的icl.cfg(如果是直接安装后使用,那么应该对应的是icl6.cfg)

增加一行,参考如下

# This Configuration file may be used for additional switches

# Enable Microsoft Visual C++* 6.0 compatibility
-Qvc6

# Path to Microsoft Visual C++* 6.0 linker
-Qlocation,link,"D:\devtool\VC\VC6\MSDEV\VC\Bin"

# *Other names and brands may be claimed as the property of others
/Zc:forScope

 

另外,支持宏 __FUNCTION__,对调试很有帮助

 

为了方便绿色使用,安装后可以提取Licenses目录和CPP目录出来,拷入绿色版的VC.如有问题,随时可以切换成原版VC6的编译工具,不错的!

6月24日

谷歌词霸不错!

感谢金山,感谢 google,用了多年的盗版词霸后,终于用上正版的词霸了.

忆微软创始人比尔·盖茨的53个难忘瞬间

新闻来源:泡泡网
微软的创始人兼董事会主席比尔盖茨即将推出微软的日常运营,全身心的投入到慈善事业中。盖茨的离开,留给人们太多的不舍,他白手起家的传奇经历至今为人乐道。而他的传奇故事背后也还有一些不为人知的经历,要我们去探究。就在这即将告别的时刻,《连线》杂志为我们再一次细数了53岁的盖茨,那些值得记忆的瞬间...

    1955年10月28日,盖茨出生于西雅图。祖母奥黛丽给他起了一个外号——“特利”(Trey),Trey是扑克术语。巧的是,盖茨后来也确实成了一名扑克牌高手。

忆微软创始人比尔盖茨的53个难忘瞬间

    1967年,六年级的盖茨向妈妈提出了一个怪问题“你是否尝试过思考?”。

    同年的秋天,盖茨的父母将他转学到了一所西雅图的男校,叫做LakesIDE School,盖茨是班上最小的学生。

忆微软创始人比尔盖茨的53个难忘瞬间

    1968年,盖茨和好友保罗艾伦开始自学Basic语言。两人几周内就用光了学校每年价值3000美元的上机时间。不过,随后两人迅速于计算机公司达成了协议,通过报告PDP-10计算机的软件缺陷来换取更多的上机时间。

    1971年,盖茨为湖边学校编写程序,包括一个设计班级课程表的程序。

    1973年9月,盖茨考入哈佛大学。他的学习成绩很不稳定。他有很多坏习惯,经常旷课去编程、玩牌,不爱洗澡,喜欢比萨饼和碳酸饮料。他与住在同一个宿舍的史蒂夫·鲍尔默(Steve Ballmer)结为好友。

    1975年1月,艾伦无意中看到《大众电子》杂志的封面,包括Altair 8800计算机的图片以及“全球第一台对抗商业模式的微型电脑”的大标题。艾伦购买了这本杂志并冲向盖茨的宿舍。几天后,盖茨致电MITS,告诉该公司他和艾伦能够为8800开发Basic程序。

    1975年2月1日,盖茨和艾伦完成8800程序开发,以3000美元的价格出售给MITS,并获得18万美元的版权费。

    1976年11月26日:盖茨和艾伦注册了名为“微软”的商标。他们曾经考虑将公司命名为“艾伦及盖茨公司”,随后又提出“Micro-Soft”的名字,不过最后他们决定将商标中的横线去掉。当时艾伦23岁,盖茨21岁。

忆微软创始人比尔盖茨的53个难忘瞬间

    1977年1月,盖茨从哈佛大学休学,在新墨西哥州阿尔伯克基创建了微软,MITS总部也设在此地。

    1977年,盖茨的秘书多次在微软办公大楼内发现他躺在地板上呼呼大睡。他仍喜欢吃比萨饼,同时对属下要求严格,经常与同事争辩。盖茨经常说的一句话是:“这是我有史以来听说的最愚蠢的事情。”

忆微软创始人比尔盖茨的53个难忘瞬间

    1977年底,盖茨多次因驾驶保时捷911超速而被警方逮捕,还有一次则是无照驾驶。这可害苦了艾伦,他保释盖茨的经历至少有一次。

    1978年12月,微软当年的年终销售额超过100万美元。

    1979年1月1日,微软将总部搬迁至华盛顿州的Bellevue。

     1980年4月28日,盖茨与IBM签署合约,同意为IBM的PC机开发软件。盖茨以5万美元的价格收购了一个名为QDOS的操作系统,对其加以改进后命名为DOS,随后授权给IBM使用。

    1981年8月12日,IBM开始销售采用MS-DOS 1.0系统的PC机。

    1982年,在投入市场的第一年,MS-DOS被授权给50家硬件厂商使用。

    1983年2月18日,艾伦因身患霍奇金氏病被迫辞去微软副总裁一职。他买了一个篮球队,创建了一个音乐博物馆,拥有世界上第三大游艇。

忆微软创始人比尔盖茨的53个难忘瞬间

    1983年11月10日,Windows揭开神秘面纱。Windows是MS-DOS的延伸,可提供图形用户界面。

    1984年1月24日,盖茨出席了Macintosh的上市宣传活动——微软是第一批为苹果电脑开发软件的公司之一。

    1985年,有传言称盖茨很恶劣的辱骂了一名女性管理人员,后者要求换岗。

    1985年8月12日,微软成立10年后,销售额达到1.4亿美元。

    1986年3月13日,微软以每股21美元的价格上市。上市当天的收盘价为28美元。微软通过上市筹集到6100万美元资金。

忆微软创始人比尔盖茨的53个难忘瞬间

    1987年,盖茨在曼哈顿召开的微软新闻发布会上邂逅梅琳达·弗朗奇(Melinda French)。

    1989年8月1日,微软推出Office办公软件。

    1990年5月13日,盖茨在母亲节当天提出微软管理层的退休时间表。

    1990年6月,联邦贸易委员会就微软和IBM在计算机软件市场的冲突展开调查。

忆微软创始人比尔盖茨的53个难忘瞬间

    1993年4月11日,在从佛罗里达州飞往西雅图的包机上,盖茨向梅琳达求婚。盖茨还安排飞机在奥马哈降落,带着梅琳达同好友沃伦·巴菲特(Warren Buffett)一同出去购物。

    1993年8月20日,美国司法部接替联邦贸易委员会,展开对微软的调查。

    1994年1月1日,比尔和梅琳达在夏威夷Lanai岛上举行了小型婚礼。盖茨邀请了梅琳达最喜爱的歌手Willie Nelson在婚礼上献唱。

    1994年4月,盖茨首次登上《连线》杂志封面。而美国政府针对微软的反垄断调查仍在继续。

    1994年7月,微软同意接受美国政府的要求,承诺放弃一些比较明显的垄断行为(例如要求硬件厂商为配置特定微处理器的所有电脑支付MS-DOS授权费,即使电脑没有安装该系统)。
    1994年11月11日,盖茨以3080万美元购得《哈默手稿(Codex Hammer)》,这是达-芬奇亲手写下的72页科学手稿。他同意将《哈默手稿》公开展示。

    1995年,盖茨出现在可口可乐(据报道他喜欢喝健怡可乐)的商业广告中:这位亿万富翁搜遍全身口袋,寻找购买饮料的零钱。

    1995年7月17日,39岁的盖茨以129亿美元的个人财富成为世界首富。微软在1995年的收入达到59亿美元,拥有17,801名员工。

    1995年8月24日,微软推出网络浏览器Internet Explorer。

忆微软创始人比尔盖茨的53个难忘瞬间

    1996年6月,《连线》杂志第二次使用盖茨作为封面人物。这一次,盖茨的照片被PS成身着泳裤的形象。这是对微软刚刚开展的媒体业务最好的写照。

    1996年12月,微软股价达到高点,同比涨幅达到88%。从帐面上看,盖茨1996年每一天的收入都在3000万美元。

    1997年10月20日,由于涉嫌违反1994年的法令,微软被要求支付每天100万美元的罚款。美国司法部表示,当硬件厂商申请Windows 95授权时,微软要求必须在硬件产品中加入IE。

忆微软创始人比尔盖茨的53个难忘瞬间

    1998年2月4日,盖茨在比利时布鲁塞尔会见比利时政府官员和企业家时,遭到奶油蛋糕的袭击。事后,盖茨开玩笑说,“这块蛋糕可不怎么好吃”。

忆微软创始人比尔盖茨的53个难忘瞬间

    1998年5月18日,美国司法部和20个州的总检察官认为微软将网络浏览器绑定到其操作系统,违反了相关规定,由此对微软提出起诉。

    1998年11月9日,在一段作证录像中,当盖茨表示自己从未蓄意将竞争对手赶出软件市场时,他的身子微微颤抖。医生据此认为盖茨可能患有亚斯伯格症候群(Asperger's syndrome)。

    1999年,盖茨和妻子将威廉·盖茨基金会更名为比尔与梅琳达·盖茨基金会,并提出了减少世界上不公平现象的目标。

    2000年1月13日,盖茨不再担任微软CEO,并将该职位交给鲍尔默。

    2000年6月7日,美国地区法官Thomas Jackson要求将微软一分为二。

    2000年11月,盖茨再次登上《连线》杂志封面,这一次是关于微软反垄断案。

    2001年6月28日,美国哥伦比亚地区上诉法庭推翻了Jackson对微软的判决。

    2002年,根据在中国内地和香港地区的一次调查,盖茨在中国的偶像地位已经超越了毛泽东。

忆微软创始人比尔盖茨的53个难忘瞬间

    2005年3月2日,盖茨在白金汉宫接受英国女王授予的骑士勋章,此前,前纽约市市长鲁迪·朱利安尼和好莱坞大导演斯蒂文·斯皮尔伯格都曾获得这一殊荣。盖茨此后有资格在名字后面加上字母“KBE”(英帝国爵级勋章)。

忆微软创始人比尔盖茨的53个难忘瞬间

    2005年12月,盖茨夫妇和爱尔兰U2乐队主唱波诺当选为美国《时代》周刊2005年度人物。

    2006年6月15日,盖茨宣布将在两年内逐渐退出公司的日常运营。

    2006年6月26日,比尔与梅琳达-盖茨基金会得到巴菲特300多亿美元捐赠,规模扩大一倍,成为世界最大的透明运营的慈善组织。

    2008年3月,在连续13年保持全球首富的地位后,盖茨在08年“福布斯富豪榜”上以580亿美元的资产总额下滑至第三的位置。而盖茨的牌友巴菲特则成为全球首富。

6月23日

90后,在思考什么呢?

不止一次跑进聊天室,看着快速滚动的文字,作为一个旁观者,有许多东西值得思考,例举一二.
 
聊天室的列表 80%以上都是所谓的脑残体,我不打算评论这些符号所带来的对语言等的影响,如此广泛的脑残现象是否说明了一些内在原因.
1.跟风,可以说这是随大流的一种行为.别人如此设置昵称,我亦如此设置.从"时尚""大众"的角度,自己不太落伍.
2.标新立异,可以说这是试图使自己区别他人的一种行为,人人都是方块字,如果我也是方块字,未免太俗气,那我就来脑残吧
那么,这两种思想是否都影响了这些人呢?
 
他们都在聊些什么呢?
这里补充一下,我所进的是青青校园这类的房间.统计了一下,60%左右都是找男友,找女友,但是这显然不是适合找异性朋友的地方.过于随便的称呼是否会使在其他场合出现呢?为什么有这么多学生出现在聊天室?他们在聊天室的时刻,首先思考的是什么呢?他们所关心的是什么?
 
 
6月22日

文凭和工作经历的对比

今天遇到一个朋友的解说,感觉甚为新奇(或者说,从未耳闻)
 
工作经验是辛苦得到的,高文凭也可能是辛苦得到的.所以应届生所付出的努力未必比工作了几年的文凭较低的人少.
 
 
6月21日

如何在Vista/Win2008上直接运行XP的安装程序

由于没有安装盘,只有硬盘文件,所以只能想办法在Win2008上直接安装XP了.

直接运行Setup.exe,安装项目变成灰色,这办法不行.

直接运行Winnt32.exe,提示有兼容性问题,被WIn2008阻止了.思考了一下,这个兼容性判断应该是根据文件名加文件的hash之类的进行判断的吧.于是,办法来了:把WInNt32.exe改个名字,例如 winnt 32.exe(仅仅是加个空格),试着运行,哇哈哈,可以进行安装了.

还是TM05最棒

最近两个月用了一系列的聊天工具,google talk,msn,QQ2007,QQ2007II,QQ2008,QQ2009,TM05,TM06,TM07,TM08,发现最适合自己的是TM05(TM06也差不多),基本功能都有了,配合珊瑚虫4.5.2正式版(TM07没有珊瑚虫增强包可用),既显IP,又能避开广告的骚扰.

google talk,没几个好友,在列表的好友也都不在线,失去即时交流的意义.不过自动在gmail保存聊天记录倒是很不错的.

msn,好友很多,但是都很少联系.所以只需要偶尔打开看看.

QQ系列,我用不到的功能远多于我需要的,还是同门兄弟TM好用,只需要能发文字,传文件就行,TM够用了.

6月10日

C与C++中的异常处理

1.     指导方针

    根据读者们的建议,经过反思,我部分修正在Part14中申明的原则:

l         只要可能,使用那些构造函数不抛异常的基类和成员子对象。

l         不要从你的构造函数中抛出任何异常。

    这次,我将思考读者的意见,C++先知们的智慧,以及我自己的新的认识和提高。然后将它们转化为指导方针来阐明和引申那些最初的原则。

    (关键字说明:我用“子对象”或“被包容对象”来表示数组中元素、无名的基类、有名的数据成员;用“包容对象”来表示数组、派生类对象或有数据成员的对象。)

 

1.1     C++的精髓

    你可能认为构造函数在遇到错误时有职责抛异常以正确地阻止包容对象的构造行为。Herb Sutter在一份私人信件中写道:

一个对象的生命期始于构造完成。

推论:一个对象当它的构造没有完成时,它从来就没存在过。

推论:通报构造失败的唯一方法是用异常来退出构造函数。

    我估计你正在做这种概念上就错误的事(“错”是因为它不符合C++的精髓),而这也正是做起来困难的原因。

    C++的精髓”是主要靠口头传授的C++神话。它是我们最初的法则,从ISO标准和实际中得出的公理。如果没有存在过这样的C++精髓的圣经,混乱将统治世界。Given that no actual canon for the Spirit exists, confusion reigns over what is and is not within the Spirit, even among presumed experts.

    CC++的精髓之一是“trust the programmer”。如同我写给Herb的:

最终,我的“完美”观点是:在错误的传播过程中将异常映射为其它形式应该是系统设计人员选定的。这么做不总是最佳的,但应该这么做。C++最强同时也是最弱的地方是你可以偏离你实际上需要的首选方法。还有一些其它被语言许可的危险影行为,取决于你是否知道你正在做什么。In the end, my "perfect" objective was to map exceptions to some other form of error propagation should a designer choose to do so. Not that it was always best to do so, but that it could be done. One of the simultaneous strengths/weaknesses of C++ is that you can deviate from the preferred path if you really need to. There are other dangerous behaviors the language tolerates, under the assumption you know what you are doing.

    C++标准经常容忍甚至许可潜在的不安全行为,但不是在这个问题上。显然,认同程序员的判断力应该服从于一个更高层次的目的(Apparently, the desire to allow programmer discretion yields to a higher purpose)。HerbC++精髓的第二个表现形式上发现了这个更高层次的目的:一个对象不是一个真正的对象(因此也是不可用的),除非它被完全构造(意味着它的所有要素也都被完全构造了)。

    看一下这个例子:

struct X

   {

   A a;

   B b;

   C c;

   void f();

   };

 

try

   {

   X x;

   x.f();

   }

catch (...)

   {

   }

 

    这里,ABC是其它的类。假设x.ax.b的构造完成了,而x.c的构造过程中抛了异常。如我们在前面几部分中看到的,语言规则规定执行这样的序列:

l         x的构造函数抛了异常

l         x.b的析构函数被调用

l         x.a的析构函数被调用

l         控制权交给异常处理函数

    这个规则符合C++的精髓。因为x.c没有完成构造,它从未成为一个对象。于是,x也从未成为一个对象,因为它的一个内部成员(x.c)从没存在过。因为没有一个对象真的存在过,所以也没有哪个需要正式地析构。

    现在假设x的构造函数不知怎么控制住了最初的异常。在这种情况下,执行序列将是:

l         x.f()被调用

l         x.c的析构函数被调用

l         x.b的析构函数被调用

l         x.a的析构函数被调用

l         x的析构函数被调用

l         控制权跳过异常处理函数向下走

于是异常将会允许析构那些从没被完全构造的对象(x.cx)。这将造成自相矛盾:一个死亡的对象是从来都没有产生过的。通过强迫构造函数抛异常,语言构造避免了这种矛盾。

 

1.2     C++的幽灵

    前面表明一个对象当且仅当它的成员被完全构造时才真的存在。但真的一个对象存在等价于被完全构造?尤其x.c的构造失败“总是”如此恶劣到x必须在真的在被产生前就死亡?

    C++语言有异常前,x的定义过程必定成功,并且x.f()的调用将被执行。代替抛异常的方法,我们将调用一个状态检测函数:

X x;

if (x.is_OK())

   x.f();

或使用一个回传状态参数:

bool is_OK;

X x(is_OK);

if (is_OK)

   x.f();

    在那个时候,我们不知何故在如x.c这样的子对象的构造失败时没有强调:这样的对象从没真的存在过。那时的设计真的这么根本错误(而我们现在绝不允许的这样行为了)? C++的精髓真的在那时是不同的?或者我们生活在梦中,没有想到过x真的没有成形、没有存在过?

    公正地说,这个问题有点过份,因为C++语言现在和过去相比已不是同样的语言。将老的(异常支持以前)的C++当作现在的C++如同将C当作C++。虽然它们有相同的语法,但语意却是不相同的。看一下:

struct X

   {

   X()

      {

      p = new T; // assume 'new' fails

      }

   void f();

   };

 

X x;

x.f();

 

    假设new语句没有成功分配一个T对象。异常支持之前的编译器(或禁止异常的现代编译器)下,new返回NULL,x的构造函数和x.f()被调用。但在异常允许后,new抛异常,x构造失败,x.f()没有被调用。同样的代码,非常不同的含意。

    在过去,对象没有自毁的能力,它们必须构造,并且依赖我们来发现它的状态。它们不处理构造失败的子对象。并且,它们不调用标准运行库中抛异常的库函数。简而言之,过去的程序和现在的程序存在于不同的世界中。我们不能期望它们对同样的错误总有同样的反应。

 

1.3     这是你的最终答案吗?

    我现在相信C++标准的行为是正确的:构造函数抛异常将析构正在处理的对象及其包容对象。我不知道C++标准委员会制订这个行为的精确原因,但我猜想是:

l         部分构造的对象将导致一些微妙的错误,因为它的使用者对其的构造程度的假设超过了实际。同样的类的不同对象将会有出乎意料的和不可预测的不同行为。

l         编译器需要额外的纪录。当一个部分构造的对象消失时,编译器要避免对它及它的部分构造的子对象调用析构函数。

l         对象被构造和对象存在的等价关系将被打破,破坏了C++的精髓。

 

1.4     对对象的使用者的指导

    异常是对象的接口的一部分。如果能够,事先准备好接口可能抛的异常集。如果一个接口没有提供异常规格申明,而且又不能从其它地方得知其异常行为,那么假设它可能在任何时候抛任意的异常。

    换句话说,准备好捕获或至少要过滤所有可能的异常。不要让任何异常在没有被预料到的情况下进入或离开你的代码;即使你只是简单地传递或重新抛出异常,也必须是经过认真选择的。

 

1.5     构造函数抛异常

    准备好所有子对象的构造函数可能抛的异常的异常集,并在你的构造函数中捕获它们。如:

struct A

   {

   A() throw(char, int);

   };

 

struct B

   {

   B() throw(int);

   };

 

struct C

   {

   C() throw(long);

   };

 

struct X

   {

   A a;

   B b;

   C c;

   X();

   };

 

    子对象构造函数的异常集是{char,int,long}。它就是X的构造函数遭遇的可能异常。如果X的构造函数未经过滤就传递这些异常,它的异常规格申明将是

X() throw(char, int, long);

但使用function try块,构造函数可以将这些异常映射为其它类型:

X() throw(unsigned)

   try

      {

      // ... X::X body

      }

   catch (...)

      {

      // map caught sub-object exceptions to another type

      throw 1U; // type unsigned

      }

    如同前面的部分所写,用户的构造函数不能阻止子对象的异常传播出去,但能控制传递出去的类型,通过将进入的异常映射为受控的传出类型(这儿是unsigned)。

 

1.6     构造函数不抛异常

    如果没有子对象的构造函数抛异常,其异常集是空,表明包容对象的构造函数不会遇到异常。唯一能确定你的构造函数不抛异常的办法是只包容不抛异常的子对象。

   如果必须包容一个可能抛异常的子对象,但仍然不想从你自己的构造函数中抛出异常,考虑使用被叫做Handle ClassPimpl的方法(“Pimpl”个双关语:pImpl或“pointer to implementation”)。长久以来被用作减短编译时间的技巧,它也提高异常安全性。

    回到前面的例子:

    class X

   {

public:

   X();

   // ...other X members

private:

   A a;

   B b;

   C c;

   };

    根据这种方法,必须将X分割为两个独立的部分。第一部分是被X的用户引用的“公有”头文件:

struct X_implementation;

 

class X

   {

public:

   X() throw();

   // ...other X members

private:

   struct X_implementation *implementation;

   };

 

    而第二部分是私有实现

struct X_implementation

   {

   A a;

   B b;

   C c;

   };

 

X::X() throw()

   {

   try

      {

      implementation = new X_implementation;

      }

   catch (...)

      {

      // ... Exception handled, but not implicitly rethrown.

      }

   }

 

// ...other X members

 

    X的构造函数捕获了构造*implementation过程(也就是构造abc的过程)中的所有异常。更进一层,如果数据成员变了,X的用户不需要重新编译,因为X的头文件没有变化。

    (反面问题:如果X::X捕获了一个异常,*implementation及至少子对象a/b/c中的一个没有完全构造。但是,包容类X的对象作为一个有效实体延续了生命期。这个X的部分构造的对象的存在违背C++精髓吗?)

    许多C++的指导手册讨论这个方法,所以我不在这儿详述了。一个极其详细的讨论出现在Herb Sutter的著作《Exceptional C++》的Items2630上。

 

1.7     对对象提供者的指导

    不要将异常体系等同于一种错误处理体系,认为它和返回错误码或设置全局变量处在同一层次上。异常根本性地改变了它周围的代码的结构和意义。它们临时地改变了程序的运行期语意,跳过了一些通常都运行的代码,并激活其它从没被运行的代码。它们强迫你的程序回应和处理可导致程序死亡的错误状态。

    因此,异常的特性和简单的错误处理大不相同。如果你不希望这些特性,或不理解这些特性,或不想将这些特性写入文档,那么不要抛异常,使用其它的错误处理体系。

    如果决定抛异常,必须明白全部的因果关系。明白你的决定对使用你的代码的人有巨大的潜在影响。你的异常是你的接口的一部分;你必须在文档中写入你的接口将抛什么异常,什么时候抛,以及为什么抛。并将这文档在异常规格申明出注释出来。

 

1.8     构造函数抛异常

    如果你的构造函数抛异常,或你(直接地或间接地)包容的某个子对象抛异常,包容你的对象的用户对象也将抛异常并因此构造失败。这就是重用你的代码的用户的代价。要确保这个代价值得。

    你没有被强迫要在构造函数里抛异常,老的方法仍然有效的。当你的构造函数遇到错误时,你必须判断这些错误是致命的还是稍有影响。抛出一个构造异常传递了一个强烈的信息:这个对象被破坏且无法修补。返回一个构造状态码表明一个不同信息:这个对象被破坏但还具有功能。

    不抛异常只是因为它是一个时髦的方法:在一个对象真的不能或不该生存时,推迟其自毁。

 

1.9     过职

    别让你的接口过职。如果知道你的接口的精确异常集,将它在异常规格申明中列举出来。否则,不提供异常规格申明。没有异常规格申明比撒谎的异常规格申明好,因为它不会欺骗用户。

    这条规则的可能例外是:模板异常。如前三部分所写,模板的编写者通常不知道可能抛出的异常。如果你的模板不提供异常规格申明,用户将降低安全感和信心。如果你的模板有异常规格申明你必须:

l         要么使用前面看过的异常安全的技巧来确保异常规格申明是精确的

l         要么在文档中写下你的模板只接受有确定特性的参数类型,并警告其它类型将导致失控(with the caveat that other types may induce interface-contract violations beyond your control)。

 

1.10     必要vs充分

    不要人为增加你的类的复杂度,只是为了适应所有可能的需求。不是所有对象都会被重用的。如pet Becker写给我的:

    现在的程序员花了太多的时间来应付可能发生的事情,而他们本应该简单地拒绝的。如果有一个抛异常的好理由的话,大胆地抛异常,并写入文档,不要创造一些精巧的方法来避免抛这些异常。增加的复杂度可能导致维护上的恶梦,超过了错误使用受限版本时遇到的痛苦。

    Pete的说法对析构函数也同样有用。看一下这条原则(从Part14引用过来的):

    不要在析构函数中抛异常。

    一般来说,符合这条原则比违背它好。但,有时不是这样的:

l         如果你准备让其他人包容你的对象,或至少不禁止别人包容你的对象,那么别在析构函数中抛异常。

l         如果你真的有理由抛异常,并且知道它违背了安全策略,那么大胆地抛异常,在文档中写入原因。

    就如同在设计的时候必须考虑异常处理,也必须考虑重用。在析构函数上申明throw()是成为一个好的子对象的必要条件,但远不充分。你必须前瞻性地考虑你的代码将遇到什么上下文,它将容忍什么、将反抗什么。如果增加了设计的复杂度,确保这些复杂度是策略的一部分,而不是脆弱的“以防万一”的保险单。

1.11     感谢

   (略)

    除了一些零星的东西,我已经完成了异常安全的主题!实际上我也几乎完成了异常的专题。下次时间暂停,在三月中将讨论很久前承诺的C++异常和Visual C++ SEH的混合使用。

6月7日

酒精能否减慢思维的步伐?

思想是否可以在沉醉时刻暂停?暂时抛开一切,回到儿时的无忧无虑.
没人可以信任,没人可以倾述,崎岖的山路独自行走.