Notepad++源码分析(1)(转载)

在网上发现了一个哥们写了关于Notepad++源码的文章,不过就写了一就没有了,我就接着他的工作再说说吧!

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

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

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

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

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

view plain

  1. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR lpszCmdLine, int nCmdShow)
  2. {
  3. HWND hNotepad_plus = ::FindWindow(Notepad_plus::getClassName(), NULL);
  4. if (hNotepad_plus)
  5. {
  6. if (::IsIconic(hNotepad_plus))
  7. ::OpenIcon(hNotepad_plus);
  8. ::SetForegroundWindow(hNotepad_plus);
  9. if (lpszCmdLine[0])
  10. {
  11. COPYDATASTRUCT copyData;
  12. copyData.dwData = 0;//(ULONG_PTR);
  13. copyData.cbData = DWORD(strlen(lpszCmdLine) + 1);
  14. copyData.lpData = lpszCmdLine;
  15. ::SendMessage(hNotepad_plus, WM_COPYDATA, (WPARAM)hInstance, (LPARAM)&copyData);
  16. }
  17. return 0; // if there has been a opended window, do not open another one!
  18. }
  19. Notepad_plus notepad_plus_plus;
  20. MSG msg;
  21. msg.wParam = 0;
  22. try {
  23. char *pPathNames = NULL;
  24. if (lpszCmdLine[0])
  25. {
  26. pPathNames = lpszCmdLine;
  27. }
  28. notepad_plus_plus.init(hInstance, NULL, pPathNames);
  29. HACCEL hAccTable = ::LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_M30_ACCELERATORS));
  30. MSG msg;
  31. msg.wParam = 0;
  32. while (::GetMessage(&msg, NULL, 0, 0))
  33. {
  34. // if the message doesn‘t belong to the notepad_plus_plus‘s dialog
  35. if (!notepad_plus_plus.isDlgMsg(&msg))
  36. {
  37. if (::TranslateAccelerator(notepad_plus_plus.getHSelf(), hAccTable, &msg) == 0)
  38. {
  39. ::TranslateMessage(&msg);
  40. ::DispatchMessage(&msg);
  41. }
  42. }
  43. }
  44. } catch(int i) {
  45. if (i == 106901)
  46. ::MessageBox(NULL, "Scintilla.init is failled!", "106901", MB_OK);
  47. else {
  48. char str[50] = "God Damn Exception : ";
  49. char code[10];
  50. itoa(i, code, 10);
  51. ::MessageBox(NULL, strcat(str, code), "int exception", MB_OK);
  52. }
  53. }
  54. catch(std::exception ex) {
  55. ::MessageBox(NULL, ex.what(), "Exception", MB_OK);
  56. }
  57. catch(...) {
  58. systemMessage("System Err");
  59. }
  60. return (UINT)msg.wParam;
  61. }

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

好,一行一行的看:

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

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

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

view plain

  1. if (hNotepad_plus)
  2. {
  3. if (::IsIconic(hNotepad_plus))
  4. ::OpenIcon(hNotepad_plus);
  5. ::SetForegroundWindow(hNotepad_plus);
  6. if (lpszCmdLine[0])
  7. {
  8. COPYDATASTRUCT copyData;
  9. copyData.dwData = 0;//(ULONG_PTR);
  10. copyData.cbData = DWORD(strlen(lpszCmdLine) + 1);
  11. copyData.lpData = lpszCmdLine;
  12. ::SendMessage(hNotepad_plus, WM_COPYDATA, (WPARAM)hInstance, (LPARAM)&copyData);
  13. }
  14. return 0;
  15. }

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

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

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

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

view plain

  1. char *pPathNames = NULL;
  2. if (lpszCmdLine[0])
  3. {
  4. pPathNames = lpszCmdLine;
  5. }
  6. notepad_plus_plus.init(hInstance, NULL, pPathNames);
  7. HACCEL hAccTable = ::LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_M30_ACCELERATORS));
  8. MSG msg;
  9. msg.wParam = 0;
  10. while (::GetMessage(&msg, NULL, 0, 0))
  11. {
  12. // if the message doesn‘t belong to the notepad_plus_plus‘s dialog
  13. if (!notepad_plus_plus.isDlgMsg(&msg))
  14. {
  15. if (::TranslateAccelerator(notepad_plus_plus.getHSelf(), hAccTable, &msg) == 0)
  16. {
  17. ::TranslateMessage(&msg);
  18. ::DispatchMessage(&msg);
  19. }
  20. }

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

view plain

  1. Window::init(hInst, parent);
  2. WNDCLASS MACOCS30EditorClass;
  3. MACOCS30EditorClass.style = 0;//CS_HREDRAW | CS_VREDRAW;
  4. MACOCS30EditorClass.lpfnWndProc = Notepad_plus_Proc;
  5. MACOCS30EditorClass.cbClsExtra = 0;
  6. MACOCS30EditorClass.cbWndExtra = 0;
  7. MACOCS30EditorClass.hInstance = _hInst;
  8. MACOCS30EditorClass.hIcon = ::LoadIcon(_hInst, MAKEINTRESOURCE(IDI_M30ICON));
  9. MACOCS30EditorClass.hCursor = NULL;
  10. MACOCS30EditorClass.hbrBackground = ::CreateSolidBrush(::GetSysColor(COLOR_MENU));
  11. MACOCS30EditorClass.lpszMenuName = MAKEINTRESOURCE(IDR_M30_MENU);
  12. MACOCS30EditorClass.lpszClassName = _className;
  13. if (!::RegisterClass(&MACOCS30EditorClass))
  14. {
  15. systemMessage("System Err");
  16. throw int(98);
  17. }
  18. _hSelf = ::CreateWindowEx(
  19. WS_EX_ACCEPTFILES,/
  20. _className,/
  21. "MACOCS 30 IDE Demonstration",/
  22. WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,/
  23. CW_USEDEFAULT, CW_USEDEFAULT,/
  24. CW_USEDEFAULT, CW_USEDEFAULT,/
  25. _hParent,/
  26. NULL,/
  27. _hInst,/
  28. (LPVOID)this); // pass the ptr of this instantiated object
  29. // for retrive it in Notepad_plus_Proc from
  30. // the CREATESTRUCT.lpCreateParams afterward.
  31. if (!_hSelf)
  32. {
  33. systemMessage("System Err");
  34. throw int(777);
  35. }
  36. if (cmdLine)
  37. {
  38. FileNamStringSpliter fnss(cmdLine);
  39. char *pFn = NULL;
  40. for (int i = 0 ; i < fnss.size() ; i++)
  41. {
  42. pFn = (char *)fnss.getFileName(i);
  43. doOpen((const char *)pFn);
  44. }
  45. }
  46. setTitle(_className);
  47. display();
  48. checkDocState();

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

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

view plain

  1. LRESULT CALLBACK Notepad_plus::Notepad_plus_Proc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
  2. {
  3. switch(Message)
  4. {
  5. case WM_NCCREATE : // First message we get the ptr of instantiated object
  6. // then stock it into GWL_USERDATA index in order to retrieve afterward
  7. {
  8. Notepad_plus *pM30ide = (Notepad_plus *)(((LPCREATESTRUCT)lParam)->lpCreateParams);
  9. pM30ide->_hSelf = hwnd;
  10. ::SetWindowLong(hwnd, GWL_USERDATA, (LONG)pM30ide);
  11. return TRUE;
  12. }
  13. default :
  14. {
  15. return ((Notepad_plus *)::GetWindowLong(hwnd, GWL_USERDATA))->runProc(hwnd, Message, wParam, lParam);
  16. }
  17. }
  18. }

这个方法里面特别处理了一个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文件中找到,为了方便大家,贴到这里:

view plain

  1. IDR_M30_ACCELERATORS ACCELERATORS
  2. BEGIN
  3. //"A",            IDM_EDIT_SELECTALL,     VIRTKEY, CONTROL
  4. //"C",            IDM_EDIT_COPY,          VIRTKEY, CONTROL
  5. "N",            IDM_FILE_NEW,           VIRTKEY, CONTROL
  6. "O",            IDM_FILE_OPEN,          VIRTKEY, CONTROL
  7. "S",            IDM_FILE_SAVE,          VIRTKEY, CONTROL
  8. //"V",            IDM_EDIT_PASTE,         VIRTKEY, CONTROL
  9. //VK_DELETE,      IDM_EDIT_DELETE,        VIRTKEY
  10. //"X",            IDM_EDIT_CUT,           VIRTKEY, CONTROL
  11. //"Y",            IDM_EDIT_REDO,          VIRTKEY, CONTROL
  12. //"Z",            IDM_EDIT_UNDO,          VIRTKEY, CONTROL
  13. "F",            IDM_EDIT_FIND,          VIRTKEY, CONTROL
  14. "H",            IDM_EDIT_REPLACE,      VIRTKEY, CONTROL
  15. VK_F3,        IDM_EDIT_FINDNEXT,    VIRTKEY
  16. END

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

首先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++都“实现”了哪些控件~那么,下次见啦!

Notepad++源码分析(1)(转载)

时间: 2024-08-06 15:39:02

Notepad++源码分析(1)(转载)的相关文章

Notepad++源码分析(2)(转载)

这次介绍NotePad++中多标签页下的鼠标拖动标签页位置的功能. 在TabBar.cpp文件中的类处理函数定义如下: [cpp] view plaincopy LRESULT TabBar::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch (Message) { case WM_LBUTTONDOWN : { ::CallWindowProc(_tabBarDefaultProc, hwnd, Me

【JAVA集合】HashMap源码分析(转载)

原文出处:http://www.cnblogs.com/chenpi/p/5280304.html 以下内容基于jdk1.7.0_79源码: 什么是HashMap 基于哈希表的一个Map接口实现,存储的对象是一个键值对对象(Entry<K,V>): HashMap补充说明 基于数组和链表实现,内部维护着一个数组table,该数组保存着每个链表的表头结点:查找时,先通过hash函数计算hash值,再根据hash值计算数组索引,然后根据索引找到链表表头结点,然后遍历查找该链表: HashMap数据

[Android实例] Scroll原理-附ScrollView源码分析 (转载)

想象一下你拿着放大镜贴很近的看一副巨大的清明上河图, 那放大镜里可以看到的内容是很有限的, 而随着放大镜的上下左右移动,就可以看到不同的内容了 android中手机屏幕就相当于这个放大镜, 而看到的内容是画在一个无限大的画布上~ 画的内容有限, 而手机屏幕可以看到的东西更有限~ 但是背景画布是无限的 如果把放大镜的移动比作scroll操作,那么可以理解,这个scroll的距离是无限制的~ 只不过scroll到有图的地方才能看到内容 参考ScrollView理解, 当child内容过长时,有一部分

java中的==、equals()、hashCode()源码分析(转载)

在java编程或者面试中经常会遇到 == .equals()的比较.自己看了看源码,结合实际的编程总结一下. 1. ==  java中的==是比较两个对象在JVM中的地址.比较好理解.看下面的代码: 1 public class ComAddr{ 2 public static void main(String[] args) throws Exception { 3 String s1 = "nihao"; 4 String s2 = "nihao"; 5 Str

Android HandlerThread 源码分析

HandlerThread 简介: 我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了.如果此时我又有一 个耗时任务需要执行,我们不得不重新创建线程去执行该耗时任务.然而,这样就存在一个性能问题:多次创建和销毁线程是很耗 系统资源的.为了解这种问题,我们可以自己构建一个循环线程Looper Thread,当有耗时任务投放到该循环线程中时,线程执行耗 时任务,执行完之后循环线程处于等待状态,直到下一个新的耗时任务被投放进来.这样一来就避免了多次

转载Aaron ---- jQuery 2.0.3 源码分析core - 选择器

jQuery 2.0.3 源码分析core - 选择器(02) 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 打开jQuery源码,一眼看去到处都充斥着正则表达式,jQuery框架的基础就是查询了,查询文档元素对象,所以狭隘的说呢,jQuery就是一个选择器,并这个基础上构建和运行查询过滤器! 工欲善其事,必先利其器,所以先从正则入手 我们来分解一个表达式 // A simple way to check for HTML strings // Prioritize

EasyUI学习总结(三)——easyloader源码分析(转载)

声明:这一篇文章是转载过来的,转载地址忘记了,原作者如果看到了,希望能够告知一声,我好加上去! easyloader模块是用来加载jquery easyui的js和css文件的,而且它可以分析模块的依赖关系,先加载依赖项.模块加载好了会调用parse模块来解析页面.把class是easyui开头的标签都转化成easyui的控件. 先看Demo1例子,再分析源代码. 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>ea

Androidbaseadapterhelper源码分析与扩展(转载)

Androidbaseadapterhelper源码分析与扩展 转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/44014941,本文出自:[张鸿洋的博客] 本篇博客是我加入Android 开源项目源码解析分析的一篇文章,初次加入,所以选了个比较简单的库,如果你曾经看过Android 快速开发系列 打造万能的ListView GridView 适配器对本篇博客就不会太陌生, base-adapter-helper就是完成类似万能

Notepad++源码编译及其分析

Notepad++是一个小巧精悍的编辑器,其使用方法我就不多说了,由于notepad++是使用c++封装的windows句柄以及api来实现的,因此对于其源码的研究有助于学习如何封装自己简单的库(当然不是MTL.MFC或者QT那样大型的库).Notepad++当前版本的源码托管在guthub上:https://github.com/notepad-plus-plus/notepad-plus-plus/releases/tag/v6.7.9.2. 下面是Notepad++源码的目录: