Notepad++源码分析(一)

来源:互联网 发布:sql注入 防御 编辑:程序博客网 时间:2024/04/29 22:13

 

    大三了,也写了一点儿程序了,但是如果只是按照自己的思路写下去恐怕难以提高,于是准备开始阅读一些开源的代码,看看别人的代码,跟别人学习学习。

    一上来就接触过于大型的项目怕是无力掌握,于是从小一点儿的开始。很早的时候我就准备读一读Notepad++这个开源项目的代码了,但是总是有别的事情,一拖再拖,现在安静下来了,就读一读吧。一开始当然从V1.0开始读啦,之后再慢慢的更新,扩大。并且,边读边记一些笔记,有一些可能看起来非常幼稚,不过确实是我所想所感的,于是记录下来,方便自己今后查阅,也方便与别的有需要的童鞋。

今天主要的任务是分析一下Notepad++启动是的动作,准备好源码(可以从SourceForge下载),设置好断点,准备开始吧!

    下载完源码之后,可以看到,Notepad++的1.0版本一共有21个cpp文件,看他们的名字基本就可以知道他们的作用了。不要忘了今天的目的,就是了解Notepad++启动时相关的动作,从开始执行分析到他的消息处理循环,今天的任务就完成了~所以,咱们果断打开winmain.cpp文件。

    话说第一眼看到这个文件我正要感叹作者真是好心人,写这么多注释,这些可爽了~而是定睛一看,居然是版权说明!代码里面干净的要死。。。只好硬着头皮一点儿一点儿的看了。。。

 

    这个文件倒也直接了当,只有一个WinMain函数,消息循环这个文件里面也有了,看来搞定这个文件咱们今天就可以收工了~如果你对WindowsAPI很熟悉的话,看这几行代码应该不成问题,可是我以前基本没用过(只用过很少几个),所以看代码也顺便学习一下API的使用了~由于这里面大量使用了API,我不可能一一列出,所以打开MSDN或者Google准备好吧~

    好,一行一行的看:

    HWND hNotepad_plus = ::FindWindow(Notepad_plus::getClassName(), NULL);

    这一行调用了FindWindow,这个API的目的是为了找到满足类名为第一个参数,而标题名为第二个参数的handle(句柄)。MSDN上面说第二个参数给NULL则匹配所有满足类名为第一个参数的句柄。

    乍看起来,这个有些奇怪。作为主函数一开始不初始化,先获取句柄,这是安得什么心?各位看官向下看就能找到答案了~

 

    这段代码是在结果不是NULL,也就是查找成功的时候执行的代码。认真看的话能看出一些名堂来了~对了,就是为了保证Notepad++只存在一个实例,如果已经打开了一个Notepad++的实例,则hNotepad_plus这个句柄必然不是NULL,这个时候如果用户再次尝试打开,或者尝试拖拽某个文件到Notepad++程序中,只会导致当前存在实例被最大化,或者开启一个新的tab来显示新打开的文件。(怎么实现的咱们之后再分析吧)各位可以试一试,找到编译生成的Notepad++程序(在这里是debug版的),分别打开两次,把拖拽文件到程序图标打开试试,就会发现跟咱们想的一样,确实只有一个Notepad++的实例存在~嗯嗯,还不赖~

    好了,由于咱们现在分析的是第一次执行的时候的状况,所以可以不考虑这一段代码,如果你设置了断点,并且单步跟踪执行的话,也会发现这一段代码没有执行~

    好,接下来进入第一次创建窗体时的部分~

    这段代码是被try起来的,我们先不考虑异常处理部分,这段代码相对还是比较好懂的~

 

    首先是,由于程序支持将文件拖拽到图标打开文件(马上就能看到怎么实现的),所以先去命令行参数的第一个参数,也就是要打开的文件名,然后调用notepad_plus_plus.init方法,这个方法值得一看,在init上面右键,转到定义!

 

    这个方法非常普通,首先调用父类Window的init方法,这个Window是自己实现的,之后咱们再分析里面有什么,为什么这样做~

然后就是填充WNDCLASS的对象,MACOCS30EditorClass。WNDCLASS的各个属性各位可以查MSDN,需要各位注意的是MACOCS30EditorClass.lpfnWndProc = Notepad_plus_Proc;这一行,这个注册的是消息处理的回调函数,至于消息驱动那一套东西我就不说了,大家可以自行Google~Notepad_plus_Proc这个函数很关键,赶紧右键转到定义一下!

 

    这个方法里面特别处理了一个WM_NCCREATE消息,把其他的消息都送给了runProc这个方法来处理。那么WM_NCCREATE这个消息为什么要如此特别的处理一下呢?

    这里干了一件事情,就是用SetWindowLong方法,把hwnd对应的GWL_USERDATA设置为Notepad_plus对象的指针。而以后再处理消息的时候,则是用GWL_USERDATA对应的这个Notepad_plus对象指针调用runProc函数。这里我学到了一招,把GWL_USERDATA作为数据存储的容器。尤其是消息处理函数给咱们的参数里面并没有Notepad_plus这种对象的指针,只有在WM_NCCREATE消息的时候会通过CREATESTRUCT对象里面的lpCreateParameter传递过来,可是咱们的runProc是定义在Notepad_plus这个类中的啊,得Notepad_plus这个对象的指针才能调用啊,所以就在第一次创建的时候把这个指针存到这个容器之中,以后就可以随意使用啦!

    好,从Notepad_plus_Proc回来,继续init之旅~接下来是用RegisterClass注册这个类的对象,然后CreateWindowEx。注意咱们之前说的那个WM_NCCREATE消息也就是这个时候触发的,clear?CreateWindowEx的第一个参数很有意思,WS_EX_ACCEPTFILES,去MSDN查一下就发现,正是因为这个属性,才使我们可以拖动文件到Notepad++里面去,好了,以后如果咱自己写的应用也要有这种效果,咱们也这么干~

    创建之后,会判断用户是否确实拖拽了一个文件以打开。这段代码也放到以后分析~

    最后是setTitle设置标题,默认设置时类名,这个类名是由一个宏定义的常量决定的:

    #define NOTEPAD_PP_CLASS_NAME"Notepad++"

    在Notepad_plus.cpp中这样赋值:

    const char Notepad_plus::_className[32] = NOTEPAD_PP_CLASS_NAME;

    所以,这里的_className也就是Notepad++!然后显示,检查文档那个的状态等等,这些咱们都以后再分析!

    之后是加载助记符~助记符的详细列表可以在资源文件的Notepad_plus.rc文件中找到,为了方便大家,贴到这里:

 

    助记符加载完毕,就进入了主消息循环,看来胜利在望啊~~

    首先GetMessage,第一件事是检测是不是对话框的消息,如果是的话就不继续处理了,对话框咱们也是以后再分析!继续向下看~

    首先来了一个TranslateAccelerator,也就是判断是不是助记符,如果是的话,返回的结果不是0,也就不继续TranslateMessage了,这个也是MSDN上面说的:an application should not call TranslateMessage if the TranslateAccelerator function returns a nonzero value. 

    最后就是消息循环啦~

    好了,今天的任务也就到此为止了~~单击执行按钮,Notepad++的1.0版本也就展现在我们眼前了~

    分析了这么多,好像感觉少了点儿什么~对!这些控件什么的怎么出来的?好,下一次就把这个作为切入点,分析一下WM_CREATE消息的处理中到底干了些什么~说不定可以顺藤摸瓜,看看都有Notepad++都“实现”了哪些控件~那么,下次见啦!

 

原创粉丝点击