原本想要学习一下语法高亮功能,做一个像Notepad++一样的编辑器。
然后就兴冲冲的下载了Notepad++的开源代码准备开始看。
但是整个框架比较复杂,看了一会之后突然发现Notepad++使用的是Scintilla这个很强大的控件,而且也是开源的。所以最后决定在分析Notepad++源代码之前先使用一下Scintilla,然后研究一下Scintilla的源码。
下面是Scintilla 的官方网站:
http://www.scintilla.org/index.html
我们下载源代码。
然后准备编译。参考了下面这个博客:
这里自己在编译makefile文件的时候出了一个错误:我使用了Visual Studio 命令提示(64位)下编译文件,结果编译出来之后的文件无法被vs识别。
一开始一直找不到错误,LoadLibrary 的返回值一直为0,后来用GetLastError()查看错误信息,才发现了原来是dll文件本身编译成了64位。
而我的程序是32位的,所以dll无法识别64位的dll。
后来用32位的 Visual Studio 命令提示重新编译出了32位的dll就对了。32位编译出来的dll是600多K, 而64位编译出来的是900多K。
具体编译的步骤:
打开Visual Studio 命令提示
进到下载好的scintilla文件目录下面的win32目录下面,
nmake -f scintilla.mak clean //清理工程
nmake -f scintilla.mak //编译
接下来主要学习下面两篇文章,一篇是参考官方的文档,另外一篇是国人写的一个翻译文档。
http://www.scintilla.org/ScintillaDoc.html
http://www.cnblogs.com/superanyi/archive/2011/04/07/2008632.html
自己学习的时候是用Win32 SDK C编写。
首先随便拉个Win32下面的应用程序,用windows程序设计里面的第一个对话框程序就好了。
我的目标是做出语法高亮,当然也不是限于这些,因为Scintilla是一个非常强大的库。
首先我们将编译好的SciLexer.dll放入当前目录中, 引入动态链接库:这里特别注意别犯像我这样的新手错误。
这个dll最大的功能就是进行语法解析 Lexer。
hmod = LoadLibrary("SciLexer.DLL");
if (hmod==NULL)
{
MessageBox(hwndParent,
"The Scintilla DLL could not be loaded.",
"Error loading Scintilla",
MB_OK | MB_ICONERROR);
}
如果hmod不为0,那么说明我们成功引入了动态链接库。
下载的源码文件里面有Include文件,包含了一些头文件。这里我们将下面两个头文件放到当前目录下。
#include "SciLexer.h"
#include "Scintilla.h"
接下来我们可以开始使用这个库里面的Scintilla类。我们可以创建一个窗口子类。
HWND hwndScintilla;
hwndScintilla = CreateWindowEx(0,
"Scintilla","", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPCHILDREN,
10,10,500,400,hwndParent,(HMENU)GuiID, hInstance,NULL);
学过子窗口控件的同学对于创建上面的代码肯定都非常熟悉了。
接下来就是发送消息了。这里发送消息有两种方式。第一个方式就是SendMessage的方式,这种方式通俗易懂。第二种方式有点复杂。下面一一来介绍下:
SendMessage(hwndScintilla,sci_command,wparam,lparam);
like:
SendMessage(hwndScintilla,SCI_CREATEDOCUMENT, 0, 0);
这样就发送了窗口消息。我们可以在父窗口的窗口回调函数里面对消息进行处理:
NMHDR *lpnmhdr;
[...]
case WM_NOTIFY:
lpnmhdr = (LPNMHDR) lParam;
if(lpnmhdr->hwndFrom==hwndScintilla)
{
switch(lpnmhdr->code)
{
case SCN_CHARADDED:
/* Hey, Scintilla just told me that a new */
/* character was added to the Edit Control.*/
/* Now i do something cool with that char. */
break;
}
}
break;
第二种方式有点复杂。我们首先看下官方文档给出的函数:
int (*fn)(void*,int,int,int);
void * ptr;
int canundo;
fn = (int (__cdecl *)(void *,int,int,int))SendMessage(
hwndScintilla,SCI_GETDIRECTFUNCTION,0,0);
ptr = (void *)SendMessage(hwndScintilla,SCI_GETDIRECTPOINTER,0,0);
canundo = fn(ptr,SCI_CANUNDO,0,0);
自己也是新手,纯粹是为了觉得语法高亮好玩才玩的。自己的C语言功底不是很好,上面这段函数有点不太好理解。
int (*fn)(void*,int,int,int);
这里应该是定义了一个函数地址,这个函数有4个参数,返回值是int类型
fn = (int (__cdecl *)(void *,int,int,int))SendMessage(
hwndScintilla,SCI_GETDIRECTFUNCTION,0,0);
这里定义了函数入口地址,相当于我们使用fn函数就是使用SendMessage函数。
不知道这样讲对不对,还请大家指教。
其实这里自己感觉也解释不太清楚,想请大家帮忙指正理解。
比如我自己是这样定义的:
int (*SendEditor)(void*,int,int,int);
//快速发送消息
SendEditor = (int (__cdecl *)(void *,int,int,int))SendMessage(
hwndScintilla,SCI_GETDIRECTFUNCTION,0,0);
ptr = (void *)SendMessage(hwndScintilla,SCI_GETDIRECTPOINTER,0,0);
canundo = SendEditor(ptr,SCI_CANUNDO,0,0);
以后自己就使用SendEditor,没有使用SendMessage了。
但是自己不理解的是:c应该没有重载 多态这些概念,我在使用SendEditor的时候里面必须写4个参数。感觉用起来也没方便多少。
自己C++没学多少。想请问下大家如何在C++下面实现该SendEditor的简洁方法?在此感谢大家。