Community
    • Login

    Generalized fix for Ctrl+C/X woes in C# plugins (beginning in Notepad++ 8.6.1)

    Scheduled Pinned Locked Moved Notepad++ & Plugin Development
    csharpplugin
    7 Posts 3 Posters 921 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Mark OlsonM
      Mark Olson
      last edited by

      TLDR: If you have a Notepad++ plugin based on kbilsted’s NotepadPlusPlusPluginPack.Net C# plugin template or my NppCSharpPluginPack template, you should look at my explanation of how to register your forms with NPPM_MODELESSDIALOG and deal with related issues.

      Shoutout to @Coises for being the first person to correctly identify the source of this trouble, and also the first person to recognize how to fix the problems with tab ordering.

      Regulars here may remember the drama and confusion surrounding Ctrl+C and Ctrl+X in plugin textboxes that started in Notepad++ 8.6.1.

      If you’re not familiar, the issue is basically as follows:

      • Notepad++ has always pre-processed keyboard shortcuts executed by all forms not registered with NPPM_MODELESSDIALOG.
      • Prior to Notepad++ 8.6.1, Notepad++ just executed the standard Windows cut/copy operations when Ctrl+X/C were pressed.
      • This mean that cut/paste previously worked normally even in forms that weren’t registered with NPPM_MODELESSDIALOG, because Notepad++ didn’t do any preprocessing of Ctrl+X/C that would cause problems.
      • In Notepad++ 8.6.1, Ctrl+X and Ctrl+C were bound to menu commands, and no longer did the normal Windows cut/copy commands.
      • Because of this change in Npp 8.6.1, Ctrl+C/X were broken in all forms that were neither (a) pop-up dialogs NOR (b) registered with NPPM_MODELESSDIALOG.

      After many hours of thrashing about in confusion, complaining bitterly to anyone who would listen, and trying one half-baked solution after another, I have finally implemented a general-purpose solution to this problem, which should properly handle all the weird corner cases your plugin might have.

      Rather than describe the solution in full here, I encourage you to read my explanation over at NppCSharpPluginPack.

      1 Reply Last reply Reply Quote 4
      • Mark OlsonM Mark Olson referenced this topic on
      • Mark OlsonM
        Mark Olson
        last edited by Mark Olson

        I’m going to list all the issues that I’ve observed so far, along with the ones mentioned above:

        • KeyUp and KeyPress event handlers no longer work consistently (I assume they only work for keys that are not considered “special” (i.e., keys other than Enter, Tab, Ctrl, etc.))
        • Hitting the Enter key no longer creates a newline in multiline textboxes (presumably because the KeyUp message has been consumed by the preprocessing here?)
          • I tried fixing this issue by setting AcceptsReturn to True for the multiline textboxes, but that did nothing.
          • the fix that actually works is shown here.
        • Hitting the Tab key does not traverse the form’s controls in TabIndex order, but instead follows the order in which the controls became visible/enabled (I think).
          • Note that this requires changes to Designer.cs (shown in NppCSharpPluginPack, but it also requires care when changing the visibility of previously invisible controls (see this code in JsonTools for an example)

        To be clear, I am not mentioning these issues to shame Don Ho, or to suggest that NPPM_MODELESSDIALOG is net negative for Notepad++. Don Ho obviously has a hard enough job as it is without having to worry about weird interactions between legacy C++ APIs and Windows Forms.

        CoisesC 2 Replies Last reply Reply Quote 2
        • CoisesC
          Coises @Mark Olson
          last edited by Coises

          @Mark-Olson said in Generalized fix for Ctrl+C/X woes in C# plugins (beginning in Notepad++ 8.6.1):

          all the issues

          I find this very troubling. I’m not familiar with C#, but just on general programming principles, when fixing one problem creates half a dozen new problems — problems that require arcane and seemingly arbitrary fixes — the first problem wasn’t fixed properly.

          As far as I can tell, in a full C#/WinForms program, the message loop isn’t exposed as such — it’s part of Application.Run. I note that Application has an OpenForms property, which implies that it “knows about” all open forms. I’m guessing it implements its own equivalent of IsDialogMessage for modeless forms, but from what we’re seeing, it must differ in some ways from IsDialogMessage.

          What I have not been able to find anywhere is a description of what C#/WinForms expects from the message loop when that loop is implemented using Win32 API.

          If someone can find an authoritative description of what should happen in the message loop to support C#/WinForms properly, we could make a strong case for extending NPPM_MODELESSDIALOG with an additional option (e.g., MODELESSDIALOGADDFORM) that would instruct Notepad++ to use message processing appropriate for a C#/WinForms modeless form instead of that for a Win32 API modeless dialog. But first we need to know (not guess) what that processing is, and be able to point to appropriate documentation.

          1 Reply Last reply Reply Quote 2
          • CoisesC
            Coises @Mark Olson
            last edited by Coises

            @Mark-Olson said in Generalized fix for Ctrl+C/X woes in C# plugins (beginning in Notepad++ 8.6.1):

            all the issues

            This is a long shot, but does setting Form.KeyPreview true on the form make any difference? I’m thinking that IsDialogMessage sends “special” keys to the dialog procedure first, and maybe if that property has the default value (false), the processing code for the form isn’t “expecting” them to come to it first.

            Mark OlsonM 1 Reply Last reply Reply Quote 1
            • Mark OlsonM
              Mark Olson @Coises
              last edited by

              @Coises said in Generalized fix for Ctrl+C/X woes in C# plugins (beginning in Notepad++ 8.6.1):

              This is a long shot, but does setting Form.KeyPreview true on the form make any difference?

              Nope, doesn’t help at all. Actually actively makes matters worse, by making my KeyUp handlers stop working.

              My current hack to solve the Enter-in-multiline-textbox problem isn’t perfect, but it’s good enough to not cause me serious annoyance, and it only took me about 20 minutes to figure out. Please don’t worry too much about it.

              If someone can find an authoritative description of what should happen in the message loop to support C#/WinForms properly, we could make a strong case for extending NPPM_MODELESSDIALOG with an additional option

              Speaking as one of the people who is suffering the most from the current state of affairs, I think making such a change would not be worth the effort, and in any case unlikely to be approved by Don Ho. As I see it, the issue seems to be that such a change would essentially set the precedent that Don Ho and the other core NPP devs are held accountable for any weird interactions between C++ APIs (which he is at least capable of understanding) and whatever random GUI framework (Tkinter? Windows Forms? WPF? whatever HTML stuff people make with Jn?) plugin makers want to use.

              While I started off really pissed off at Don Ho for causing these issues for me, I think the fact that Notepad++ can be pretty easily extended in just about any language is awesome, and I don’t want to penalize Don Ho for making Notepad++ so extensible.

              rdipardoR 1 Reply Last reply Reply Quote 0
              • rdipardoR
                rdipardo @Mark Olson
                last edited by rdipardo

                @Mark-Olson said in Generalized fix for Ctrl+C/X woes in C# plugins (beginning in Notepad++ 8.6.1):

                I think the fact that Notepad++ can be pretty easily extended in just about any language is awesome, and I don’t want to penalize Don Ho for making Notepad++ so extensible.

                You are thanking the wrong person, I think. Don Ho obviously took for granted that every plugin would be developed on the same toolchain as the host application. And C++ developers have mostly vindicated that assumption by using Microsoft’s Visual C++ compiler almost exclusively. Plugins built with GCC are even harder to find than non-C++ ones.

                The developers of the first .NET plugin template are the ones who bridged the gap between .NET Framework and the Win32 interface, just as Damjan Cvetko wrote the bindings that virtually every Object Pascal plugin still uses today. All of it has been a third-party effort — ad majorem gloriam de Notepad++ — no thanks to Don, even though his user base has benefited immensely from it.

                In the long run, keeping pace with upstream developments is not sustainable for any third party, unless you’re developing plugins professionally or with plenty of sponsorship. It’s even less sustainable when you have to contend with bizarre and subtle incompatibilities that probably can’t be fixed without rewriting the component library, or at least sub-classing every off-the-shelf component [^1]. And even then you still have all the technical debt of an originally 32-bit template that can’t marshal a notification structure without writing out of bounds.

                My advice to anybody considering a new .NET plugin would be to learn C++. I would say the same if they were thinking about an Object Pascal plugin, but of course that will never happen. Pascal is the most clumsily explicit language you will ever see. Coding in it is like building with LEGO: the structure may be coherent, but the pieces never blend together; it’s all rectilinear edges and rough little knobs. To Pascal’s credit, it’s only a short remove from C, so the compiler, debugger and component library can directly interface with system APIs. But C++ already does that, with fewer abstractions, and without having to statically link an entire runtime.


                [^1] A typical stack trace, captured by Visual Studio, revealing that every class derived from Control implements its own WndProc method(!):

                > JsonTools.dll!JSON_Tools.Forms.FindReplaceForm.FindReplaceForm_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e) Line 74	C#
                  System.Windows.Forms.dll!System.Windows.Forms.Control.OnKeyUp(System.Windows.Forms.KeyEventArgs e)	Unknown
                  System.Windows.Forms.dll!System.Windows.Forms.TextBox.OnKeyUp(System.Windows.Forms.KeyEventArgs e)	Unknown
                  System.Windows.Forms.dll!System.Windows.Forms.Control.ProcessKeyEventArgs(ref System.Windows.Forms.Message m)	Unknown
                  System.Windows.Forms.dll!System.Windows.Forms.Control.WmKeyChar(ref System.Windows.Forms.Message m)	Unknown
                  System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m)	Unknown
                  System.Windows.Forms.dll!System.Windows.Forms.TextBoxBase.WndProc(ref System.Windows.Forms.Message m)	Unknown
                  System.Windows.Forms.dll!System.Windows.Forms.TextBox.WndProc(ref System.Windows.Forms.Message m)	Unknown
                  System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam)	Unknown
                
                CoisesC 1 Reply Last reply Reply Quote 1
                • CoisesC
                  Coises @rdipardo
                  last edited by

                  @rdipardo said in Generalized fix for Ctrl+C/X woes in C# plugins (beginning in Notepad++ 8.6.1):

                  Don Ho obviously took for granted that every plugin would be developed on the same toolchain as the host application. And C++ developers have mostly vindicated that assumption by using Microsoft’s Visual C++ compiler almost exclusively. Plugins built with GCC are even harder to find than non-C++ ones.

                  I don’t draw the same conclusion. It’s possible that I’ve missed something, but as far as I can tell, the plugin interface is strictly C (not C++) and Win32 API. That is as close to generic as you can get under Windows.

                  Were the interface C++, a GCC plugin probably wouldn’t work. The C ABI is compatible across compilers (and languages); C++ ABIs are not. (They’re not even guaranteed to be compatible between different versions of the same compiler, though generally compiler vendors try really hard not to break things in that way.)

                  When writing in C++ for Windows only and using the Win32 API, Visual Studio with MSVC is simply the path of least resistance. Unless you have another tool chain already set up, or additional requirements, there is little reason to use GCC, so I’m not surprised it isn’t frequently done. There’s no reason it can’t be done, it’s just extra hassle for no benefit.

                  I suspect the problem for C# plugins is, strictly speaking, WinForms and not C# itself. WinForms was probably not meant to interoperate with unmanaged Win32 API windows in the same application (especially with Win32 handling the top level windows and the message loop).

                  If that’s the problem, and if the creators and maintainers of C#/WinForms have not specified any mechanism by which this can be reliably accomplished, then there’s little anyone can do about it except hack around and hope, or refrain from trying to use C#/WinForms for something it is not meant to do.

                  1 Reply Last reply Reply Quote 3
                  • First post
                    Last post
                  The Community of users of the Notepad++ text editor.
                  Powered by NodeBB | Contributors