2010年4月13日
VC++ 2008 Express版本默认不携带MFC,这给使用带来了很大的不方便,下面的两篇文章介绍了添加MFC的方法。
http://www.cppblog.com/xcpp/archive/2009/04/24/vc2008express_atlmfcwtl.htmlhttp://www.codeproject.com/KB/MFC/MFCinVisualStudioExpress.aspx其中第一篇还介绍了怎样加入WTL,第二篇为英文,介绍了怎样加入VC6版本的MFC,也就是MFC4.2.
基本上跟加入其他界面库车差不多,拷贝文件+添加路径。据第二篇文章作者所述,VC++ 2008 Express中使用MFC不能编译DEBUG版,不能使用静态链接,以下为原文:
Further, you will not be able to run with the MFC debug DLLs, nor will you be able to link statically against MFC. Using dynamic linking in release mode makes everything run fine, though. Finally, the Express edition does not come with the drag and drop MFC resource editor. You can either edit your resource files (these are the files that determine how your windows look like) by hand in text mode, or you can try an external program.
暂时没有环境试验,应该没有什么问题。不过如第二篇作者所言不能使用DEBUG的话,应用价值会打折扣。
2009年10月23日
摘要: (接上)当你调用free()释放上述8字节的内存时, crt首先会用0xdddddddd填充全部48字节的内存块(包括簿记信息), 这样就可以通过检查这块内存的值获知这块内存在释放后是否右被写入过(比如使用野指针写内存).
接下来, crt通常会调用HeapFree()函数将本内存块归还给win32堆, win32堆会将本内存块填充为0xFEEEFEEE. 注意, crt并不维护...
阅读全文
2009年10月20日
这两天老得同时开好几个VC和BCB调试程序, 机器慢的要命, 每次构建都得要好一会, 而且BCB6在我手里老莫名其妙的出现不能单步的情况, 重新编译一遍就好了. 老盯着电脑看编译器的output或者盯着浏览器刷renren.com也不是办法. 就从网上找了一篇关于crt调试堆的文章, 里面说得比较详细的一篇文章, 也浅显易懂.
我发现拿零碎的时间翻译或者整理点小东西蛮不错, 不然平时两三分钟的时间根本不知道该拿来干什么.
原文见: http://www.nobugs.org/developer/win32/debug_crt_heap.html
在DeviceStudio的Debug编译模式下, crt中的堆内存分配操作----包括malloc()和free()----使用一个特殊的, 便于调试的版本, 我们称之为crt debug堆(译注: 下面简称CDH). 相比于电光火石(译注: 原文blazingly, 我想不出更确切的说法)的运行效率, 调试版本更关注对于堆错误的定位, 它通过以下三种手法实现以上诉求:
1.用守护内存块包围新分配的内存, 这样就可以侦测到缓冲过载和欠载. 所谓守护内存块就是一系列被填充为0xfd的内存字节, 又被称为”无主之地”.
2.用一个特殊的值(0xcd)初始化新申请的内存.
3.同时用一个特殊的值填充(0xdd) 被释放的内存.
为了便于记忆, 可以这样理解以上用于填充的值:
1.0xcd意为Clean Memory.
2.0xdd意为Dead Memory.
3.0xfd意为Fences(译注: 栅栏).
CDH将大部分工作交由win32 api中的堆函数HeapAlloc()和HeapFree()完成, 每进程4Gb的虚地址空间的分块和管理是由kernel32.dl中的Win32堆自己完成的.
当你调用malloc(8)分配8字节的内存时, CDH会调用HeapAlloc()申请48字节的内存, 额外的40字节被用来存放内存块的额外信息----比如调用malloc()的源文件和行号, 以及指向上/下一个内存块的指针.在后面的列表中, 所有的crt调试信息均被标记为红色.
HeaoAlloc()本身也需要记录簿记(译注: 原文bookkeeping)信息, 事实上,一个HeaoAlloc()调用会在进程地址空间里保留80字节内存, 其中8字节的簿记信息出现在真正使用的40字节之前, 剩下的32字节在真正使用的40字节之后.在下面的列表里, Win32堆簿记信息被标记为灰色.最后, HeapAlloc()返回的内存总是被一4字节对齐初始化为0xBAADF00D.顺道说一句: 当你使用经由VirtualAlloc()管理内存的虚拟内存管理器申请内存页时, 获取的内存页会被初始化为0, 所以可以断定HeapAlloc()按照上述模式执行了额外的初始化工作.
crt取得40字节的内存块后会填入自己的簿记信息. 头两个字(译注: 即WORD)用来存放直向”前一个”和”后一个”crt堆内存块的指针. 这里的前后不能从字面去理解, 因为所谓指向”后一个”内存块的指针事实上指向的是时间顺序上紧邻本内存块之前分配的内存块, 相应的, 指向“前一个”的指针指向的是下一个将被分配的内存块. 之所以这样命名, 是因为内存块链表是从最后分配的内存块开始的. 同时, 为了使堆检查代码能遍历每个内存块, CDH还保存着第一块和最后一块内存的地址(_pFirstBlock和_pLastBlock).
如果调用malloc()代码所在的文件名和行号是已知的, 它们将被被保存在第三第四个字中, 紧接着下面一个字表示本块申请了多少字节内存. 再下面一个字是类型域, 等于1表示new或malloc()分配的普通块, 2表示crt分配的供内部使用的块. 0表示已经被用户释放但是还未归还给win32堆的块(详见下文). 通常来说, 新申请的内存块本位置等于1. 最后一块是计数器, 每执行一次内存分配计数器加1.
通过malloc()得到的8字节内存无用内存包围. 这些空内存被填充为0xfd, 当整个内存块被free()时, crt会检查这些空内存存放的值是否仍然是0xfd. 如果值改变了, 说明程序有错误存在. 不幸的是, 错误可能发生的远比检查到的早, 如果需要更精确的检查, 可以使用Purify或者Boundschecker, 他们能让程序停止在内存崩溃的点上, 如果你不想花钱, 你可以等我写篇文章告诉你怎样巧妙的实现这个功能.
真正被使用的8字节内存被初始化为0xcd, 如果你的对象中间出现连续的0xcd, 那么你一定是忘记了初始化一些东西.
(未完待续)
2009年10月12日
这不是一篇非技术文章.
我不知道我算不算是cppblog上实力最差的家伙, 但是在cppblog或者cnblogs上看文章, 我确实经常产生这样的想法. 这里有人可以从脚本引擎到远程调用框架一路"发明"下来, 大有再造一个.NET的气势; 还有人能自己写出复杂的3D动画然后自己渲染出来, 做出来的效果我看跟我在网吧见过的网游也差不许多(如果不是这样, 那么抱歉, 我从来没学会过玩任何一种游戏, 包括扑克牌); 更有甚者以做算法题为乐, 可能他就是我高中的时候听说的那些搞电脑竞赛保送了的人吧......好吧, 我也搞竞赛来着, 不过不是电脑竞赛.
这些我都做不来.
我之所以能干写程序这行, 主要是因为一年半以前我被我的导师发配到现在公司帮忙, 在那里我花了大概两个月的时间看了<c++ primer>70%左右. 是的我没有读完, 在我读完之前负责带我的一个同事让我用C++ Builder 6写一个计算器, 别误会, 不是那种带parser的计算器, 就是一个窗口上有几个按钮的那种. 我写出来了, 但是写的并不轻松, 我得先学会怎么装IDE(是的你猜对了我在看<c++ primer>期间一行代码都没编译过), 装控件包, 装插件, 建工程, 配置等等. 然后我花了一些时间搞明白什么是C++ Builder, 怎么拖控件, 怎么设置控件属性. 然后还要在每个控件双击产生的函数体里写很多if/else/switch这些对我来说并不算熟悉的语句, 而在此之前, 我连注册表都没打开过. 说实话搞定这些没有给我一点成就感----我是个讨厌自学讨厌到深恶痛绝的人...但是我考试蛮厉害的:)
然后他们就开始让我跟他们一起写软件!!!
我当时并没有意识到这是个极其可怕的事实, 但是现在我经常会做恶梦梦到这个----这家公司真NB.
我啰嗦这么多就是为了让你能清楚的想象到----我的同事(我还在这家公司)今天来问我问题----这是一个什么情景.
OK, 我们来说点和程序有关的, 他来问我的是这么个问题: 我所在的公司同时维护着三四套cs模式的软件, 都是BCB写的, 日常的主要工作就是系统集成和定制开发. 现在我可爱的同事负责的项目中客户要求有把一个渲染视频的子窗体(TVideoPanel)全屏显示的功能, 就像暴风影音那样. 我这同事把TVideoPanel的Parent设成NULL, 然后调整尺寸. 结果发现视频不显示了, 调试了很多遍都是这样. 然后他叫了几个同事包括我过去帮他看看, 有人说是VCL的bug, 有人说是引用的动态库的bug, 还有人说DirectDraw的bug. 反正普遍反映早就发现过这个问题, 都没解决. 中午吃饭以后我对此有点好奇, 就拿出自己做得一个播放库来简单写了一个带全屏功能的demo, 发现确实不行, 全屏以后视频就不显示了. 到播放库里单步了一下发现问题所在了: 全屏以后, ::IsWindow(hWnd)返回错误, 也就是说句柄已经失效了. 这就很明显了----VCL把窗口关了. 在VCL的delphi源码里查找了一下, 确认是这个原因, 你每次改变控件Parent属性的时候VCL都会把相应的窗体关掉再在新的父窗体上家里一个. 于是乎DDraw的表面也好, VFW的HDC也好自然都不管用了.
到了下午我跟我同事说你用API操作吧别用VCL的属性了. 试了一下, OK. 然后大家又凑过来感慨了一番.
现在这是一幅名副其实的技术文章了.
这样的事情在我身边, 或者说在千千万万个我所在的这样的公司(所谓"这样"的公司是指没有对内部用编程工具了解的比较深刻的员工更妄论socket/DB/编译器/算法/行业知识...的公司)里每天都在发生着. 但是它不会在blog上发生, 这里只有高手和中手, 各自发布着自己的成果或者讨论着比较深刻的编程问题. 你们的文章我都会看, 也确实能学到不少东西(比如我所有关于设计模式的只是都是在blog上看到的, 后来我买了那本经典的<设计模式>但我看不懂, 我又借了一本C#版的<大话设计模式>, 仍然看不懂...), 但是看得多了我就觉得很疑惑: 高手们, 你们都在哪儿? 我怎么在现实中就没见过你们呢? 那些精通3D的人, 那些深谙算法的人, 那些读过STL/BOOST/ACE/Qt...源码的人, 他们都做着什么样的工作? 我虽然是个边缘的程序员, 但我的环境也算不上边缘, 我公司边上有一堆外包企业, 我还见过sisco的人, NI的人, TI的人, moto的人, simens的人, Philips的人, MileStone的人, Varint的人, 华为的人, 中兴的人, h3c的人, 南瑞的人...这些都是做软件的人. 但是我没见过网上那些英明神武的人, 从来没见过.
所以我不得不惭愧的承认, 网络上给了我一个虚幻的程序员世界. 是的, 尽管这里有一些妙趣横生的东西, 但还是要回到现实. VCL控件为什么不能随便设置Parent才是现实.
不过我还是要感谢网络, 至少它让我知道高手是个什么样子, 什么样的东西写出来是牛B的, 什么样的东西写出来是baby talk. 并且它给我一种冲动, 一种"发明轮子"的冲动, 它对我说: 看, 那里分明有乐趣在闪光.
2009年9月29日
刚刚调试软件发现了一个bug, 又是由野指针引起的. 确切的说, 应该是又又又...有tmd的的野指针引起的.
根本原因我把一个本来该由Manager统一分配销毁的类的构造和析构函数函数放在了public域, 然后在稍微有点复杂的逻辑中我想Manager申请了一个对象, 后面有好几个分支都要释放这个对象, 终于在一个分支里我脑残了, 直接delete掉了这个对象.
我发现身边的很多人, 包括我在内, 在面对C++驳杂的特性和很多编程规则时都有一种懒惰的情绪, 觉得那是繁文冗节, 不想去碰它们.
对象只能由谁来构造谁来析构重要么? 我写外部逻辑的时候注意点不就行了?
多暴露几个接口也没什么吧? 反正都是我自己写的我又不会误用.
接口之间调用顺序的依赖也没什么吧? 自己知道就行了.
组合和继承? 还是继承吧, 继承比较省代码.
const不const也没什么所谓, 反正我不修改这个参数就是了.
单例? 全局变量好了. 我的程序小.
设计一个状态模式提取解析协议? 脑子不够用啊, for循环加switch吧.
......
很多使用C++的规则和前人的建议都是为了安全而设立的. 的确, 很多规则你不注意它, 也能把功能写出来. 但是以违反了一条, 你就给自己增加了一条保证安全的负担, 最后你的编程将如履薄冰, 说不定哪个角落你没注意, 程序就挂掉了. 到时候排错可不是那么容易, 内存和线程里的bug哪个都能耗费上你几小时甚至几天的时光.
所以说, 遵守C++的游戏规则是安全使用C++的前提.
2009年9月15日
一个人花8块钱买了一只鸡,9块钱卖掉了,然后他觉得不划算,花10块钱又买回来了,11块钱卖给另外一个人了。问他最后的收益是多少?
忘掉那些关于鸡的成本变化以及本该得的利润之类的阐述吧,那些自诩为正确的家伙很明显没看出这个人在干什么:很明显,他在炒鸡!
假设A花8块钱买了一只鸡,9块钱卖给B,10块钱买回来,11块钱卖给C。
好好的8块钱的鸡,怎么C就这么大头要11块钱买去呢?因为他老婆要生了要喝鸡汤?OK,回到经济的语境:因为他看到鸡在升值。
再来看看鸡是怎么升值的。A花8块钱买了一只鸡,说明鸡的市价是8块,转眼之间,9块钱卖给了B。B有病么?不会直接去供应商那里买出厂价的鸡?没有这种大头,除非A就是B。也就是说,A在玩自产自销的把戏。这还不够,转眼间这只鸡以10块钱的价格在A的两只手间又走了一遭。这时候这个C坐不住了,他看不到A和B是谁,就看到鸡越来越贵了。再加上很多诸如“禽流感有蔓延趋势,鸡供应紧俏”之类的怂恿,于是他进入这个博傻游戏,觉得总会有比自己傻的人买单,殊不知他自己就是那个买单的人。
金融产品市场里,这种傻C有的是。
2009年8月16日
2009年8月13日
手上有一个动态库client.dll,其中有一个初始化函数BOOL Init(). 我写一个exe直接调用Init()函数返回成功。我自己写一个dll提供一个BOOL Init_Wrapper()函数,功能就是简单的调用Init,在写一个exe调用Init_Wrapper函数,返回还是成功。
但是我把这个client.dll封装进实际项目里的一个动态库device.dll,在里面调用Init,在项目主程序里使用,这时候Init就返回失败。
有没有人遇到这种问题?我知道这个问题问的没头没脑的,但是我现在确实完全找不到可能的方向。
有没有可能是引用库的版本问题?我看到client.dll引用WS2_32, mfc42.dll, 等等。
2009年8月12日
如果有个设备商提供给你一个SDK,里面有这样一个函数:
typedef void (__stdcall *fCallback)(long lHandle);
HRESULT EXPORT FunctionX(int i, int j, int k, fCallback pCallback);
第一回合:使用时发现怎么调用FunctionX结果都是失败,打电话询问相关研发人员,得知这个函数成功返回TRUE,继续问:那这里这个HRESULT没什么用是吧?答曰:其实要是研究windows比较深的话,就知道HRESULT就是long。
第二回合:终于知道怎么调用了,然后又发现pCallback回调不管用,根本就不回调。又打电话询问。答曰:哦,我还以为这个功能根本用不到呢,没实现,你没看见我们的Demo里pCallback都是NULL么?
Help me.
2009年8月7日
最近要改一个视频播放的程序, 原来因为原来只是一个定制的功能,所以随便搞完了就交差了.没考虑什么效率之类的东西,画图部分用的的DrawDibDraw函数.
最近要求进化这个功能,要在15-25fps下绘制百万像素的视频帧.
那原来的程序试了一下,够呛,CPU占用率不大好看.于是想用DDraw7重新做一下画图部分,看能不能让效率高一点.
研究了一下DDraw的接口,基本的画图流程比还是较简单的,就是:
1. 建立主表面和后台表面.
2. do
{
后台表面锁屏, 把RGB序列memcpy到后台表面锁住的ddsd里的缓冲中.
主表面->Blt(..., 后台表面, ...).
}
while(...)
按照上面的流程写出程序,发现画出来的图是乱掉的. 于是猜想要么是RGB序列排列不对,要么是色深问题.很快排除掉前一个,确定是色深问题.
但是就是这个色深问题难住我了: 我把后台表面设置成RGB24,mask也设置了,发现还是不行,查了一下发现Blt不能完成色深的转换.开什么玩笑?不是要让我自己转吧?这可是百万像素实时图像啊...要是自己随便写一个转色深的循环,估计还没有VFW的方法快.
谁解决过这个问题啊?帮个忙.