Archive for the 'C++' Category

为什么不用SEH

前些时候,在论坛上看到一个朋友说SEH怎么怎么不好,一定不要用它。其实存在即合理。就像GOTO,不能因为它破坏了程序的流程,就不用它,适当的使用,还是可以事半功倍的。

大家知道SEH是Windows操作系统提供的一种异常处理机制,它和C++无关。在Compiler编译的时候,就把这个机制加入了我们的程序中。在VC下可以用__try, __finally, __except, __leave等关键字来标识。由于SEH可以捕获硬件异常(Hardware Exception)和软件异常(Software Exception),它比C++的异常机制能捕获更多的异常,所以有朋友不喜欢这点,认为它掩盖了错误。其实这种说法是也是合情合理的,毕竟掩盖错误不是最好的解决方案,找出问题的所在才是我们应该做的。可是在现实中,我们不可能找到所有的bug,或者由于时间的关系,来不及修补这个bug,不如先用SEH挡一挡,何尝不可。

就像我之前的一个项目,程序在一个地方偶尔会Crash掉,而且这个地方如果不能正常执行丝毫不影响整个程序的运作,不会对用户造成损失,在找出问题真正的原因之前,我们完全可以用SEH捕获异常。

 下面的例子也是一个SEH优势的体现

BOOL SafeDiv(INT32 dividend, INT32 divisor, INT32 *pResult)
{
    __try
    {
        *pResult = dividend / divisor;
    }
    __except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO ?
             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
        return FALSE;
    }
    return TRUE;
}

SafeDiv用来做除法操作,它的返回值指出函数是否执行成功。pResult指向最终的结果,如果不用SEH,这类Hardware Exception会导致程序Crash,这里引入了SEH,我们在发现有除零错误的时候,让函数返回FALSE,调用处通过检查函数的返回值就可以判断除法运算是否成功,没必要就因为一个除零错误导致程序Crash掉。

由于SEH是Windows操作系统特有的机制,所以它不适合用在那些跨平台的代码,这种情况不用也罢,既然SEH是一个Windows的一个很好的异常处理机制,我们虽不能滥用它,适当的、合理的使用还是值得推荐的。

P.S.
For details about SEH, check this: http://www.google.com/search?hl=en&q=Programming+Applications+for+Microsoft+Windows

API Hook的问题,卡住了:(

这两天研究如何在我自己的进程中,获取IE或者FireFox中的网页内容,也就是监视用户上网信息。需要通过API Hook的方式拦截IE(拦截FireFox也是一个道理)用的API。

 刚开始的思路是,我想Hook MSHTML.dll 中的API,用Depends看了一下,发现只有寥寥几个导出函数,诸如ShowHTMLDialog(), 这些都不能获取网页内容。那么再换个DLL呢,于是Depends一下,ShDocVw.dll,导出函数倒是不少,大部分名字都是N/A,只有几个如OpenURL(), AddUrlToFavorites(),对我也没什么用。

上面的方法不行,我只能再向底层靠了,准备Hook Ws2_32.dll,Depends这个DLL,你可以看到,N多导出函数,让人赏心悦目,其中recv就是我要Hook的API,写了一个Demo程序,然后打开IE,运行这个Demo,发现毫无反应,我开始怀疑是我的API Hook的方式不对,后来我用同样的方法,发现可以拦截住user32.dll的MessageBoxA,百思不得其解。Google一下,发现有人和我遇到类似的问题:

Hook MSN Messenger之socket通訊的鳥事

于是这次我挂到wsock32.dll,果然,这次有反应了。总算前进了一小步,离我要获得网页内容还有很多距离呢,我的recv函数中也就是my_recv内容如下:

int PASCAL FAR my_recv (SOCKET s, char FAR * buf, int len, int flags)
{
        OutputDebugString(_T(“In my_recv”));
 
        OutputDebugString(buf);

        SaveLog(buf, len);

        int nReturn = 0;
        myJmp.SetHookOff();
        nReturn = recv(s, buf, len, flags);
        myJmp.SetHookOn();

        return (nReturn);
}

打印出buf的内容,用DebugView可以看到,很多都是乱码。不知道别人都怎么分析recv到的数据流的,怎么才能从中获取文本内容。接下来,保存也是一个问题,SaveLog的第二个参数是buf的长度,我觉得应该填recv的返回值,它是实际返回的数据的长度。但是把SaveLog放到recv后面,就不能保存到文件中,放到前面就可以,没办法,这里我只能用len。

打开文件如下图:

2007-07-07_00-14-02.jpg

最后还有个问题是:卸载这个钩子的时候,偶尔会让IE崩溃。下面是我的一个demo,有兴趣的可以帮忙调试看看上面的若干个问题该怎么解决。写的仓促,代码很乱,我也没去测试Release是否有问题,暂且这么着吧。

Downlaod Demo

A New Release of Yet Another Browser

I’ve changed my plan, Yet Another Browser(YAB for short) still looks like its sibling, if a brower has a MS-Word-like look-and-feel, is it weird? So far, I think YAB will be helpful for some guys. If you’re addicted to online photo communities, like Flickr, it can help you download photos when you go surfing, you don’t need to save the photos manually. Just one click, all photos are flying to your disk on autopilot.

Features:

  1. You can tune up the opacity of main window
  2. Multi-tab support
  3. Right-click Close-Button to click all tabs, Left-Click just close one.
  4. Your can rename the title of YAB, it will show what you want on task bar.
  5. You can set the length of tab’s title.
  6. Keep a list of websites you recently closed, you can easily undo it.
  7. Just one-click to download all images in current tab
  8. You can change the default direcotry where images will be saved.

Todo:

  1. Boss key
  2. Regular-Expressions for downloading images
  3. Password Protection
  4. Favorites
  5. Clear hisotry
  6. Clear dropdown list
  7. Self-update
  8. i18n
  9. Auto-downloading images when you go surfing, even you don’t need one-click.
  10. You can replace the icon of YAB with another one.

Homepage for YAB: http://charry.org/m/myapps/YetAnotherBrowser 
Stay tuned.

TabIndex != TabPages.IndexOf

TabControl的有个属性叫TabIndex,每个从Control派生来的控件都有这个属性,这个Tab是Tab键的顺序(MSDN说:A control with a lower tab index receives focus before a control with a higher index.)

我正好需要获取TabControl中某个Tab Page的Index。这些Tab Page保存在TabControl的TabPages中,它是一个集合,实现了IList接口,其中有个方法就是IndexOf (Determines the index of a specific item in the IList.)。

鬼使神差的,我把TabIndex属性当成了Tab Page在TabPages中的Index了,花费了N久时间,才发现问题所在。Tab Index和IndexOf,TabPages,这几个词放在一起,一不小心就会搞错,错误总在不经意间。

Yet Another Browser

If you’re a office worker, are you afraid of being busted by your boss when surfing? I make this semi-browser from scratch, it resembles a regular Microsoft Office Suit Application,  I’ll add the following features to the browser, e.g.:

  1. Transparent window
  2. One-click to hide
  3. Download all images to your disk

I created it with C#,  it’s easy to create your own browser with its powerful components, everyone can make it if he wishes.

Download it! (Make sure you have the .NET Framework 2.0 installed on your computer)

A parser for Netscape Bookmark File Format

There’re many kinds of bookmark formats for browsers, Netscapte Bookmark File Format is a popular one. it has a long story, the HTML-like bookmark is not easy to parse, XML is the best alternative, if I’ve got choice. but the well-known browser FireFox still use this format. In order to parse it, you have to do it using the basic string operation, like ‘find’, ‘substring’, etc. I wrote a small app for parsing bookmarks.html which is located in “…\Application Data\Mozilla\Firefox”, the project was built with VS2005(UNICODE version). Follow this link to download the project files.

I’v got a question: How to sort IE’s favorites, IE saves its favorites in a directory, but the order is saved in Windows Registry([HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\MenuOrder\Favorites]). The binary data is saved as private data format, AFAIK there is no articles about this format on MSDN. I saw some articles illustrating how to ‘GUESS’ the real meaning of it. but guessing is not the perfect solution, if you know about this, please tell me :) .

建立自己的消息循环(续)

前面的帖子《建立自己的消息循环》中提到使用从CCmdTarget派生的类作为消息传递的载体,但这种方法不能传递多余的参数(也许可以,但我不知道怎么搞:)。为了使这个机制支持大多数的消息,我把前面的例子改动了一下,把那些用于消息处理的类,改为从CWnd派生,然后再Create这些窗口。这样这些类其实就是个标准的窗口了,只是我没有把它们显示出来。

本例和前面的例子,主要用在那些多使用消息机制的Project中,其实也可以不必这么做,完全可以直接去调用类的方法。但是如果消息机制比较适合,此方法不失为一种选择。

点击这里下载Demo

建立自己的消息循环

在MFC程序中使用消息机制的时候,常常会在程序即将完成的时候发现,在某个窗口中加入了太多的消息处理宏,并且这些消息的Handler和UI界面的方法混合在一起,待到项目规模大的时候,会导致后续的维护越来越困难。我们希望将UI的动作和业务处理的动作隔离开,互不影响,我们可以模仿MFC的消息流转机制,就本文来说,是Command的流转(Command Routing),大家知道,所有的从CCmdTarget派生的类都可以支持消息处理,比如CDocument, CView, CDocTemplate等。Framework在内部使用了Chain of Responsiblity。消息会在一个链条上找到自己的执行的地方,我们可以把不同的业务逻辑分割为各个不同的环,我们可以在修改一个环的情况下,而不去影响其他的环。附件中的例子只是简单的发送一个WM_COMMAND消息,消息的ID,表示了不同的动作。此方法适合发送消息时候,参数很少或者不用参数的情况。

 示例代码的简单说明:

  1. 业务处理类需要从CCmdTarget派生
  2. 在每个业务处理类里面加入适当的消息映射(注意更新MyCommand.h中的消息的宏)
  3. 在CCmdManager类的BuildCmdTarget()中加入类的初始化
  4. 在主窗口的OnCmdMsg中调用 CCmdManager的CallOnCmdMsg方法

P.S.
这里写的只是MFC消息机制最粗糙的改造,由于时间的关系,这里不做过多的介绍

示例代码,点击这里下载

Add crash report to your application

By Charry 

编写‘零缺陷’的软件是人们追求的目标,然而要做到真正的‘零缺陷’是不太可能的任务,即使的最简单的程序,我们也不敢保证它是一个理想的’bug-free’的产品。bug会贯穿一个产品的每个阶段。在开发阶段我们可以debug我们的代码,但到了后期,当我们把程序release给用户时,如果程序发生的问题,比如Crash,我们应该收集程序Crash的原因,以帮助我们修补程序的bug,现今有很多软件都提供了”Crash Report”的功能,最常见的就是MS的一些产品,在程序崩溃的时候,会出现一个对话框,提示我们程序崩溃了,是否愿意将崩溃时候的一些信息发给MS,以供开发人员调试之用。

且不说用户反馈的信息是否对我们有用,但说崩溃后提示:“程序崩溃,请blah, blah, blah”这样的字样,从某种意义上说就是在增强用户体验,因为比起程序咣当一声倒地身亡,这样起码给用户一个暗示:问题是有解决的途径的。

CodeProject上有篇文章详细的介绍了Crash Report,http://www.codeproject.com/debug/XCrashReportPt1.asp 。不过文章中介绍的内容是针对VC6的,对于VS2005还是有些问题的。我总结了一下,如何给VS2005的工程加入Crash Reprot的支持(如果是VC6的工程,请直接参考上面的链接)。

首先,去http://www.codeproject.com/debug/XCrashReportPt4.asp 下载DEMO程序,我们需要其中的XCrashReport,将它编译后,会生成XCrashReport.EXE。然后将

ExceptionAttacher.cpp
ExceptionHandler.cpp - 需要去除它的预编译头文件开关
ExceptionHandler.h
GetWinVer.cpp
GetWinVer.h
MiniVersion.cpp
MiniVersion.h
CrashFileNames.h

这个几个文件加入到工程中。注意,直接加入工程中并不能编译通过,作者在他的文章中提到了:“The Unicode implementation is not complete or tested.”,好在修改上面的错误也很简单,这里就不赘述了。然后将工程切换到release模式并打开工程的属性。先找到”C/C++->General”,把”Debug Information Format”设置为”Program Database(/Zi)”;然后找到”Linker->Debugging”,把”Generate Debug Info”设置为”Yes(/DEBUG)”;最后,找到”Linker->Command Line”,增加一个选项”/OPT:REF”。(好像VS2005缺省就是这样的设置)

保存好后,就可以编译您的工程了,把XCrashReport.exe 和你的EXE程序放到一起,这样在程序崩溃的时候,就会自动生成Crash Report。对于VC6编译出来的EXE程序,我们可以按照作者文章中介绍的那样,直接用VC IDE找到引发Crash的代码行,可是在VS2005下,我尝试了N次,始终不行。为了定位出错的代码行,我们还需要再下载一个工具,就是WinDbg。这是MS开发的一个非常有用的DEBUG工具,安装完毕后,将XCrashReport.EXE生成的”CRASH.DMP”的拖到WinDbg中,然后选择”File->Source File Path”设置工程源码的目录,选择”File->Symbol File Path”指向最后一次编译生成的pdb文件所在的目录,接下来在”View”菜单中打开”Call Stack”窗口,最后在”Command”窗口中输入:.ecxr 并回车,WinDbg就会显示Call Stack和引发Crash的代码。

最后补充一点,XCrashReport需要dbghelp.dll,Windows自带的dbghelp.dll可能无法正常使用,最好将原作者的DEMO中的dbghelp.dll一并copy了。还有填写接受report的email的时候,不要简单的写一个email,那样outlook不能正常的发送,得在email前面加上SMTP,例如:SMTP:test@hotmail.com 。

提醒一下:一定要把VC/VS2005生成的pdb文件备份好,以及和这个pdb文件对应的代码,如果代码改变了,WinDbg就不能跳到正确的代码行。

完!

A class for simulating MSN Messenger popup window

This is a dialog-based class derived from CDialog, it shows or hides a dialog in an animated way, just like MSN Messenger does. it’s still a draft, I’ll add more features and refactor it later on.

Usage:

  1. Add “AnimateDlg.h” and “AnimateDlg.cpp” in your project.
  2. Add a CDialog-based class in your program.
  3. Replace all ‘CDialog’ with ‘CAnimateDlg’ in header file and implementation file.

Known Bugs:

This verstion of CAnimateDlg only works properly when Windows task bar is on the bottom of screen. it’ll be fixed in a future version.

To be continued…

Download project

Page 1 of 212