使用C#开发Notepad++插件

      Notepad++是Windows平台上的一个强大的开源文本编辑器。它支持插件扩展,有很多第三方插件可以实现各种强大的功能,比如FunctionList函数列表,Compare文件比较,NppExec编译代码。我们也可以自己编写插件实现我们想要的功能。

       Notepad++本身是C++编写,因此可以使用C++编写插件,但是我们也可以C#和Ada编写,因为已经有大牛封装了相应的SDK。编写插件十分简单,在官网(http://notepad-plus-plus.org/contribute/plugin-howto.html)下载对应语言的Visual Studio工程模板,然后只需要四步:

1、  定义插件名称

2、  定义插件命令编号

3、  自定义插件命令

4、  定义插件命令相关函数


      前些天用Perl写了Verilog代码缩进脚本,鉴于很多Xilinx ISE用于习惯使用Notepad++作为代码编辑器,因此自己开发了一个插件,将Perl脚本的功能集成进去,原理是使用C#调用外部Perl脚本,并将打印的处理结果重定向回来,最后输出到Notepad++中。因为网络上C#开发NPP插件的知识较少,便这里介绍一下插件开发的基本过程。


      本文使用官网提供的NppPlugins.NET进行插件开发。

      首先下载NppPlugins.NET

      解压下载的文件,将Visual Studio Project Template C#目录中的NppPlugin.zip(不要解压)文件拷贝到My Documents\Visual Studio 20**\Templates\ProjectTemplates\VisualC#\目录。

      在VS2005/VS2008/VS2010(本文使用的是VS2010 英文旗舰版)中新建项目,就可以看到Notepad++插件的选项,如下(注意不要使用中文版VS2010,实测会出现加载插件dll错误的情况,比如Unknown Exception等错误,这时可以使用dumpbin工具查看dll导出接口函数只有6个中的3个或几个,函数不完整,生成的dll有问题,后来我使用英文版的VS2010也会出现这个错误,不知道为什么):

正常的导出函数有如下6个:

       新建完工程之后,解决方案管理器中会出现如下文件结构:

       其中,DllExportAttribute.cs用于定义DllExport的特性,使用该特性的非托管C#函数,将可以被转换成非托管的函数,便于Notepad++进行调用。

插件dll需要导出6个函数给Notepad++调用,不过这些都已经在模板中实现了,打开UnmanagedExports.cs文件就可以看到。其中isUnicode函数表明该插件适用于Unicode版本的Notepad++(好像还有个ANCII版本的Notepad++,不过不常用,以前的一个版本,如果你使用的是中文版的VS2010,生成的插件dll加载时可能提示不兼容Unicode版本Notepad++,此处需要留意)。

      将托管dll转成非托管dll是通过一个target实现的,我们可以打开工程目录下的DllExport目录,看到还有3个dll和一个.targets文件。.targets文件是微软MSBuild中的一种文件格式,具体语法、功能可参看http://msdn.microsoft.com/zh-cn/library/ms164312.aspx大致功能是通过前面3个dll将托管dll转换成非托管dll。



      我们再打开Main.cs进行查看,前面定义了一个字符串常量PluginName就是插件名称(也就是显示在插件菜单中的名称)。CommandMenuInit函数是用于初始化菜单,每个菜单对应一个命令,其中下面两句代码表明添加了两个菜单项:

PluginBase.SetCommand(0, "MyMenuCommand",myMenuFunction, new ShortcutKey(false, false, false, Keys.None));
PluginBase.SetCommand(1, "MyDockableDialog",myDockableDialog); idMyDlg = 1;

      菜单命令分别对应myMenuFunction处理函数和myDockableDialog函数,后面一个可以在Notepad++中显示一个窗口。待会我们可以看看效果。当然模板中的代码还做了比如添加快捷键和工具栏快捷按钮的操作,这里不做多的讲解。

       myMenuFunction函数工作很少,只是显示一个MessageBox对话框。

       myDockableDialog是创建了一个Winform窗口,这个窗口可以集成到Notepad++中去。在代码的Forms目录我们可以看到设计的窗口名称为frmMyDlg,我们可以使用标准的Winform设计方法去编辑这个窗口的样式和功能,因此非常方便。

      由于Notepad++的核心是开源的Scintilla编辑器,在NppPluginNETBase.cs和NppPluginNETHelper.cs中实现了对Notepad++和Scintilla的API调用接口。Scintilla基于Win32的消息机制,对其的控制都是通过发送消息实现,比如获取编辑器内的文本,设置文本,设置前景色和背景色等样式,进行缩放操作等,NppPluginNETHelper.cs中定义了这些消息的参数,NppMsg是Notepad++本身的消息的枚举,SciMsg是Scintilla的消息枚举。

       如果我们需要获取当前编辑器内的文本,我们可以这样操作:

    public static stringGetDocumentText(IntPtr curScintilla)
    {
        int length =(int)Win32.SendMessage(curScintilla, SciMsg.SCI_GETLENGTH, 0, 0) + 1;
        StringBuilder sb = newStringBuilder(length);
        Win32.SendMessage(curScintilla,SciMsg.SCI_GETTEXT, length, sb);
        return sb.ToString();
    }
    public static string GetCurrentDocumentText()
    {
        IntPtr curScintilla =PluginBase.GetCurrentScintilla();
        returnGetDocumentText(curScintilla);
    }

    不过,这里有一些东西需要小心。在VS2010模板中,导入的win32的dll转换成C#的函数的SendMessage如下:

    [DllImport("user32")]
    public staticextern IntPtrSendMessage(IntPtr hWnd, SciMsg Msg, intwParam, [MarshalAs(UnmanagedType.LPStr)]StringBuilder lParam);

    最后一个参数类型是StringBuilder,C++中对应Char*,因为获取文本,我们需要传出一个参数,这个StringBuilder就用于接收传回的文本字符串。可是如果你尝试往编辑器文本中添加一些中文,调试时你会发现,获取到的中文发生了乱码。如果你比较有经验,会很快知道这是文件编码的问题,由于编码可能不一致,使用Stringbuilder接收时,默认转换成C#的Unicode编码,因此会出现乱码。Notepad++支持多种文件编码,如下:

    因此,你在获取文本内容的时候,需要确定当前文件使用的文件编码类型,一般会使用ANSI/Unicode/UTF8/GB2312等。Scintilla默认使用ANSI编码,你可以查看Notepad++右下角的状态提示确定当前文本使用的编码类型:

    另外,Scintilla提供了SciMsg.SCI_GETCODEPAGE消息,用于获取当前编辑器文本的编码代号(CodePage,代码页),比如UTF-8 的代码页是65001。实现代码如下:

IntPtr codepage =Win32.SendMessage(curScintilla, SciMsg.SCI_GETCODEPAGE, 0, 0);

    获取了编码之后,我们还需要做一件事,就是把之前的StringBuilder类型改成byte[],下面是我修改后的函数:

public static string GetDocumentText(IntPtr curScintilla)
{
    int length =(int)Win32.SendMessage(curScintilla, SciMsg.SCI_GETLENGTH, 0, 0) + 1;
    byte[] buffer = new byte[length];
    Win32.SendMessage(curScintilla,SciMsg.SCI_GETTEXT, length, buffer);
    //获取编辑器的编码
    IntPtr codepage =Win32.SendMessage(curScintilla, SciMsg.SCI_GETCODEPAGE, 0, 0);
    cp = (int)codepage;
    //根据不同的编码选择不同的解码方式
    MessageBox.Show(codepage.ToString());
    string re = string.Empty;
    switch ((int)codepage)
    {
        case 936:
            re =Encoding.Default.GetString(buffer);
            break;
        case(int)SciMsg.SC_CHARSET_ANSI:
            re =Encoding.Default.GetString(buffer);
            break;
        case(int)SciMsg.SC_CHARSET_GB2312:
            re =Encoding.Unicode.GetString(buffer);
            break;
        case (int)SciMsg.SC_CP_UTF8:
            re =Encoding.UTF8.GetString(buffer);
            break;
        default:
            MessageBox.Show("不支持的编码类型(非UTF8/GB2312/ANSI)");
            re = string.Empty;
            break;
    }
    return re;
}

    说了这么多,我们现在来编译一下工程并测试一下。将编译生成的MyNppPlugin1.dll拷贝到Notepad++插件目录C:\Program Files (x86)\Notepad++\plugins,启动Notepad++,查看插件菜单,是否多出MyNppPlugin1菜单,并出现两个子菜单。


    分别点击子菜单,出现如下效果,与预期的效果一致:

            


相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页