NppPlugin.NET v0.5

UFO
2011-03-10
2012-11-14
1 2 > >> (Page 1 of 2)
  • UFO
    UFO
    2011-03-10

    Hi,

    here's another major update:

    Forget all previous versions of this package - this is a brand new approach!
    No more C/C++ code (plugin loader) required at all. Huh? How come?
    Here's the explanation - all credits for this technique to following guys:
    -Dark Daskin: http://www.codeproject.com/KB/dotnet/DllExporter.aspx
    -Robert Giesecke: http://sites.google.com/site/robertgiesecke/Home/uploads/csharpprojecttemplateforunmanagedexports

    This package should contain 2 folders:

    1) Visual Studio Project Template C#:
        A VS template for very fast and even easier building of .NET plugins for Notepad++.
        Setting up a plugin for N++ has never ever been as easy as with this package.
        Please See its containing text file for further information.

    2) Demo Plugin:
        An example .NET plugin for Notepad++, build upon the template above.
        It demonstrates the same functionality as the original demo plugin by Don HO:
        http://notepad-plus.sourceforge.net/commun/pluginDemoTemplate/NppPluginTemplate.zip
        I don't know if I've added new bugs, but I've corrected some small mistakes which
        are in the original demo. I've also added example code for registering icons for
        the tab of a dockable dialog and for Notepad++'s tool bar (and how to toogle its
        state).

    Both template and demo come with solution files for:
       . Visual Studio 2005
       . Visual Studio 2008
       . Visual Studio 2010

    Download:

    https://sourceforge.net/projects/sourcecookifier/files/other%20plugins/

    Enjoy
    UFO

     
  • bbluemel
    bbluemel
    2011-03-10

    Hi UFO,

    Haven't checked it out yet, but nice one :) I looked at Robert Giesecke's approach a few weeks back for doing this, and one issue I ran into was the inability to step through and debug the plugin (which is possible with your previous loader - thankfully), if i recall this was because the post build script decompiles and recompiles the DLL putting the exports in.   Which meant I gave up on the idea :(

    Have you overcome this at all in the template?

    Cheers

    Ben

     
  • UFO
    UFO
    2011-03-10

    hi,

    I tried it all in VS2005/2008/2010 and was able to debug w/o any issues.
    What comes to mind is that maybe you tried debugging something with
    VS2010 when it was compiled with the 2.0 toolset. Afaik you can't, but
    that's an own issue. Then you can code/debug with a high toolset, but
    still release with 2.0.

    So far I only noticed positive things. Everything starts up noticeable faster
    than with the loader approach. But I still wanna compare the performance..

    greets

     
  • bbluemel
    bbluemel
    2011-03-10

    Hi UFO,

    Just tried it with your demo one, works fine, I must have been doing something wrong.  Time to port my new plugin to 0.5 then :D

    Cheers

    Ben

     
  • Don HO
    Don HO
    2011-03-11

    @ UFO :

    I have only 4 words for your nppPlugin.NET v0.5 :

    EX-CE-LLEN-T !

    Now I'm coding the hash functions in C# with only 4-6 lines instead of hundreds of lines.

    The only problem is SCI_GETSELTEXT message :
    I have a pdf file loaded in Notepad++, which contains several NULL characters.
    In order to compute the hash of the whole PDF file or its partial data, the hash plugin get selected data which contains NULL characters via SCI_GETSELTEXT message.
    I use StringBuilder as the 4th parameter in Win32.SendMessage() call:

    _ int start = (int)Win32.SendMessage(hCurrScintilla, SciMsg.SCI_GETSELECTIONSTART, 0, 0);
    int end = (int)Win32.SendMessage(hCurrScintilla, SciMsg.SCI_GETSELECTIONEND, 0, 0);
    int textLen = end - start;
    StringBuilder sb = new StringBuilder(textLen + 1);
    Win32.SendMessage(hCurrScintilla, SciMsg.SCI_GETSELTEXT, 0, sb);_

    That works fine with ASCII string but not binary data, because StringBuilder object truncks data due to the 1st NULL character.

    I'm sure that SCI_GETSELTEXT copies the whole data (with NULL characters) since I did it in ASCII/Hex converter plugin (in C++).
    Have you any idea to get the selected binary data by using nppPlugin.NET?

    Don

     
  • UFO
    UFO
    2011-03-11

    hi,

    well, I don't know as which type you need the result, but here are some thoughts in C#:

                IntPtr hCurrScintilla = PluginBase.GetCurrentScintilla();
                int textLen = (int)Win32.SendMessage(hCurrScintilla, SciMsg.SCI_GETSELTEXT, 0, 0);
                IntPtr ptrText = Marshal.AllocHGlobal(textLen);
                Win32.SendMessage(hCurrScintilla, SciMsg.SCI_GETSELTEXT, 0, ptrText);
                // get it as string but truncated..
                string s = Marshal.PtrToStringAnsi(ptrText);
                // or everything.. well as byte array
                byte[] b = new byte[textLen];
                Marshal.Copy(ptrText, b, 0, textLen);
                
                Marshal.FreeHGlobal(ptrText);
    

    greets

     
  • Don HO
    Don HO
    2011-03-11

    Thank you UFO, it works perfectly.

    I added your NppPlugin.NET download link in:
    http://notepad-plus-plus.org/plugin-howto

    If you want to have a intro in your site or elaborate the tutorial/usage of NppPlugin.NET in the same page, please let me know.

    Don

     
  • UFO
    UFO
    2011-03-12

    Thanks,
    I will see what I can set up..
    cheers

     
  • CFrank
    CFrank
    2011-03-15

    Hi,
    is meant to work with visual basic as well?

    I have converted your example, imported the additional step
        <Import Project="DLLExport\NppPlugin.DllExport.targets" />
    into my vbproj file, copied the additional dlls into the dllExport directory
    but I don't see the ILDasm and ILAsm commands get executed like in csharp.
    Do you know what I might have done wrong?

    Thank you
    Claudia

     
  • CFrank
    CFrank
    2011-03-15

    Hi,
    I have some additional informations.I found out how to get more informations on build process.
    When build is done using vb, I get
    Target "AfterBuild" in file "D:\Projects\\NppPluginNET\DLLExport\NppPlugin.DllExport.targets":
      Task "DllExportTask"
      Done executing task "DllExportTask".
    Done building target "AfterBuild" in project "NppPluginNET.vbproj".
    whereas I get a lot more on csharp like (i truncated the line to make readable)
    Target "AfterBuild" in file ….:
      Using "DllExportTask" task from assembly …..VS2008\DllExport\NppPlugin.DllExport.MSBuild.dll".
      Task "DllExportTask"
        ILDasm: calling …
        ILDasm: ildasm …
        ILAsm: Found method: NppPluginNET.UnmanagedExports..method private hidebysig static bool  'isUnicode'() cil managed
        ILAsm: Removing NppPlugin.DllExport.DllExportAttribute from NppPluginNET.UnmanagedExports.isUnicode
    .
    .
    .
        Resolving local member refs: 0 -> 0 defs, 0 refs, 0 unresolved
        Writing PE file
        Operation completed successfully
       
    .
    .

      Done executing task "DllExportTask".
    Done building target "AfterBuild" in project "NppManagedPluginDemo.VS2008.csproj"

    Any ideas?
    Thank you
    Claudia

     
  • UFO
    UFO
    2011-03-19

    hi,

    I still don't have a VS with installed VB.NET component at hand,
    but I will try to add VB.NET support in the next package (and I'm
    sure it will work).
    Personally even more important for me is getting this package
    compiled with SharpDevelop again..

     
  • CFrank
    CFrank
    2011-03-20

    Hi ufo,
    thank you very much.
    Looking forward to the next package.
    Claudia

     

  • Anonymous
    2011-03-23

    Hi, great work on this project, I'm porting one of my Visual Studio extensions (http://tinyurl.com/alignby) to Notepad++, when I saw I could still use c#, I was over the moon. However I've been getting a strange error - FatalExceptionEngineError - often when I run the extension twice in a row (not always, but consistent if the document contents is the same). The error occurs before it even touches my code (although, as I said it's on the 2nd call) The calls I use are SCI_GETLINECOUNT, SCI_GETSELECTIONNSTART, SCI_GETSELECTIONNEND, SCI_LINEFROMPOSITION, SCI_GETLINE, SCI_POSITIONFROMLINE, SCI_INSERTTEXT (all on CurrentScintilla).

    P.S. Also I noted adding a post build event (like coping the addin to the plugin folder) causes it to not build correctly and Notepad++ complains it's an ANSI plugin

     
  • UFO
    UFO
    2011-03-23

    Regarding the post built event:
    Usually you simply define the plugin folder as the project's output folder.
    No need for additional actions.

    And the FatalExceptionEngineError.. will it be open source to have a look at the issue?
    Maybe somebody else knows more about SCI calls and their traps, but for me it's enough information.

    greets

     
  • UFO
    UFO
    2011-03-23

    /EDIT
    lol I meant NOT enough information

     
  • CFrank
    CFrank
    2011-03-23

    Hi https://www.google.com/accounts ;-)
    regarding the ANSI complains, I assume that the necessary ILDAsm and ILAsm steps didn't run.
    You can easily check this by running dependency walker on the generated dll.
    If you see exported functions like IsUnicode you should have a vaild "c-"dll.

    To extend the verbosity on the output - goto tools-options-projects and solutions-build and run
    and set MSBuild… to Diagnostic for example.

    Hope this help to identify the cause of your problem.
    Sorry for my bad english - I'm not a native speaker.

    Claudia

     

  • Anonymous
    2011-03-24

    Thanks, though a bit of testing I figured out it was caused by SciMsg.SCI_GETLINE when you don't provide the StringBuffer with an initial capacity.
    Strange, but easy fix.

     

  • Anonymous
    2011-03-24

    Sorry StringBuilder, haven't used Java in ages, don't know why I had a relapse

     

  • Anonymous
    2011-04-22

    Hi guys,
    I am the one who wrote the MSBuild task that allows you to create unmanaged exports.
    @Ufo
    Pretty cool thing you have created. This is exactly the reason why I have published this build task.
    There're a lot of apps out there with just a plain DLL plugin interface, and it's up to guys like you to let C# folks write their stuff as well. :-)

    Some pointers to some questions in this thread:

    @Debugging problems: You probably did a release build or something like that.
    Without a pdb, IlDasm won't be able to get the line numbers. So that after re-assembling, all debug infos are gone.
    The IlDasm/IlAsm tool chain is pretty stable and shouldn't create this kind of problem on its own for a debug build. I did go through quite some lengths to ensure that I do not mangle your IL or debug infos in any way. I even use those infos to pinpoint error messages to the exact method that has a problem.

    So, if you have debugging problems, I would be very interested to hear about them. You can reach me via mail using robert dot giesecke at google's mail service.
    Probably should put up a google group forum kinda thing some time… ;-)

    @SharpDevelop
    I am using plain MsBuild, so it should work just fine with #d.
    However, it could be that #D is not providing all environmental infos to the MsBuild engine like VS is doing.
    So, if you do run into problems with #d, I'd like to hear about that, too. Chances are, that they are just providing the same infos using a different property name.

    @VB.Net
    It is entirely possible to have this working with VB.Net as well. But I would strongly suggest to reference an assembly that contains the DllExportAttribute.
    The reason is, that VB.Net's creepy way of trying to comfort old VB classic users can result in a lot of problems when you have to have tight control over how your code ends up as IL. The problem here is, that VB.Net will mess up your namespaces. Which makes it hard to have a class with the full name of RGiesecke.DllExport.DllExportAttribute.
    You can get around this, by telling my task that another Attribute class shall be used as the "export marker".
    This is done so by creating a property in your project file like this <DllExportAttributeFullName>Your.Name.Space.YourAttributeClass</DllExportAttributeFullName>.
    I use ducktyping to read the properties that are set on the attribute, so you could just recreate what you are seeing in the DllExportAttribute.cs file.

    However, it is by far easier to just reference the RGiesecke.DllExport.DllExportAttribute.dll, though. (You can find it here http://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports, it's in the attached archive)
    In this case, I will remove the reference to this assembly after my task is done (the attributes in you methods will be removed as well), so you do not need to redistribute this assembly along yours.

    Long story short:
    - grab RGiesecke.DllExport.DllExportAttribute.dll from the archive at the link above
    - reference it
    - Follow the steps on that page, so that you have the DllExport subfolder in your project and added the task to your project file
    - decorate your methods with DllExport
    - have fun with it

     
  • UFO
    UFO
    2011-04-23

    Hi Robert,

    thx for the explanations! I will add your notes about the debugging problems
    into the package next time. Regarding #D and VB.NET you probably did not
    see the current version of this package:
    https://sourceforge.net/projects/notepad-plus/forums/forum/482781/topic/4429157

    greets and thx again

     
  • Gordon Burgett
    Gordon Burgett
    2011-10-06

    1st off: Love this template.  Thanks!

    Since I'm very new to Windows messages and np++ plugins, I had a bit of trouble finding out how to get notifications.  To make it easier, I created a helper with events for notifications.  Maybe you'd like to use it in the next version of this template.

        public static class NppNotification
        {
            public static event NppNotificationEvent Ready;
            public static event NppNotificationEvent TBModification;
            public static event NppNotificationEvent FileBeforeClose;
            public static event NppNotificationEvent FileClosed;
            public static event NppNotificationEvent FileBeforeOpen;
            public static event NppNotificationEvent FileOpened;
            public static event NppNotificationEvent FileBeforeSave;
            public static event NppNotificationEvent FileSaved;
            public static event NppNotificationEvent Shutdown;
            public static event NppNotificationEvent BufferActivated;
            public static event NppNotificationEvent LangChanged;
            public static event NppNotificationEvent WordStylesUpdated;
            public static event NppNotificationEvent ShortcutRemapped;
            public static event NppNotificationEvent FileBeforeLoad;
            public static event NppNotificationEvent FileLoadFailed;
            public static event NppNotificationEvent DocOrderChanged;
            internal static void Process(SCNotification nc)
            {
                switch(nc.nmhdr.code)
                {
                    case (uint)NppMsg.NPPN_DOCORDERCHANGED:
                        if (DocOrderChanged != null)
                            DocOrderChanged(nc);
                        break;
                    case (uint)NppMsg.NPPN_FILEBEFORELOAD:
                        if (FileBeforeLoad != null)
                            FileBeforeLoad(nc);
                        break;
                    case (uint)NppMsg.NPPN_SHORTCUTREMAPPED:
                        if (ShortcutRemapped != null)
                            ShortcutRemapped(nc);
                        break;
                    case (uint)NppMsg.NPPN_WORDSTYLESUPDATED:
                        if (WordStylesUpdated != null)
                            WordStylesUpdated(nc);
                        break;
                    case (uint)NppMsg.NPPN_LANGCHANGED:
                        if (LangChanged != null)
                            LangChanged(nc);
                        break;
                    case (uint)NppMsg.NPPN_BUFFERACTIVATED:
                        if (BufferActivated != null)
                            BufferActivated(nc);
                        break;
                    case (uint)NppMsg.NPPN_FILEBEFORESAVE:
                        if (FileSaved != null)
                            FileSaved(nc);
                        break;
                    case (uint)NppMsg.NPPN_FILEOPENED:
                        if (FileOpened != null)
                            FileOpened(nc);
                        break;
                    case (uint)NppMsg.NPPN_FILEBEFOREOPEN:
                        if (FileBeforeOpen != null)
                            FileBeforeOpen(nc);
                        break;
                    case (uint)NppMsg.NPPN_FILECLOSED:
                        if (FileClosed != null)
                            FileClosed(nc);
                        break;
                    case (uint)NppMsg.NPPN_TBMODIFICATION:
                        if (TBModification != null)
                            TBModification(nc);
                        break;
                    case (uint)NppMsg.NPPN_READY:
                        if (Ready != null)
                            Ready(nc);
                        break;
                    case (uint)NppMsg.NPPN_SHUTDOWN:
                        if (Shutdown != null)
                            Shutdown(nc);
                        break;
                    case (uint)NppMsg.NPPN_FILEBEFORECLOSE:
                        if (FileBeforeClose != null)
                            FileBeforeClose(nc);
                        break;
                }
            }
        }
        public delegate void NppNotificationEvent(SCNotification nc);
    

    and then in UnmanagedExports.cs:

            [DllExport(CallingConvention = CallingConvention.Cdecl)]
            static void beNotified(IntPtr notifyCode)
            {
                SCNotification nc = (SCNotification)Marshal.PtrToStructure(notifyCode, typeof(SCNotification));
                NppNotification.Process(nc);   //process the notification to fire off the associated event
                if (nc.nmhdr.code == (uint)NppMsg.NPPN_TBMODIFICATION)
                {
                    PluginBase._funcItems.RefreshItems();
                    Main.SetToolBarIcon();
                }
                else if (nc.nmhdr.code == (uint)NppMsg.NPPN_SHUTDOWN)
                {   
                    Main.PluginCleanUp();
                    Marshal.FreeHGlobal(_ptrPluginName);
                }
            }
    
     

  • Anonymous
    2011-10-11

    Out of interest is there any reason you haven't made an assembly that contains most of this logic and distributing it through NuGet? This would allow people devs to make sure they have the latest version easily.

     
  • Tao Klerks
    Tao Klerks
    2011-12-28

    Hs anyone found a good way of referencing external managed assemblies that aren't registered in the GAC? Typically, I've followed either of 2 approaches:
    - Merge assemblies using ILMerge, or
    - Place additional assemblies in the same folder as the target assembly or program

    ILMerge isn't working (because there's unmanaged code in the plugin assembly as a result of the additional MSBuild steps I guess) - maybe reordering this process to have the ILMerge process happen first would work. I'll report back when I've looked into that.

    In terms of placing the addiional assemblies in the same folder as the plugin assembly, there are two problems:
    - Notepad++ tries to load all dlls in that folder as plugins, and complains if they're not
    - It looks like the search path for auto-resolving assembly paths is not including the plugins folder anyway. To get the assemblies loaded correctly I need to place them in the main notepad++ program folder (which is impolite :))

    So far, I can think of two approaches that I haven't looked into yet:
    - Placing the assemblies in an arbitrary subfolder of plugins, and loading them via reflection - should work but could be expensive
    - Attempting to have the assemblies merged before the DllExport MSBuild stuff gets to them - I'm guessing I'd have to create my own custom MSBuild task, which certainly sounds like a headache.

    Does anyone have any experience with either of these approaches, or other thoughts?

     
  • UFO
    UFO
    2011-12-28

    Imho easiest is following approach:

    Add a method like this one:

            internal static void SetupDomain()
            {
                Assembly pluginAssembly = Assembly.GetExecutingAssembly();
                string pluginPath = pluginAssembly.Location;
                string pluginName = Path.GetFileNameWithoutExtension(pluginPath);
                string pluginSubFolder = Path.Combine(Path.GetDirectoryName(pluginPath), pluginName);
                AppDomain.CurrentDomain.AppendPrivatePath(pluginSubFolder);
            }
    

    Now you can place your referenced stuff into a subfolder, which has the same name
    as your plugin. If your main DLL is called "MyPlugin.dll", then your subfolder should
    be called "MyPlugin". It's a sensemaking but unwritten (?) convention followed by
    myself and other plugin authors aswell.

    Of course there's still the warning CS0618. I personally add following at top of the source file:

    #pragma warning disable 618
    

    cheers

     
  • UFO
    UFO
    2011-12-28

    /EDIT:
    It's not 'clean' because of the warning, but I guess I should
    add the code in the template next time anyway. Unless somebody
    comes up with a cleaner/sexier solution of course?

     
1 2 > >> (Page 1 of 2)