How to get the scintilla view0/view1 HWNDs



  • I’m trying to write an external app which will try to automate Notepad++ with the same SendMessage interface that plugins use. However, I am having trouble figuring out how to grab the HWND for the two views’ Scintilla editors.

    I tried looking through the messages at http://docs.notepad-plus-plus.org/index.php/Messages_And_Notifications, but (aside from having to use google’s cache, because of the recent website issues), I cannot find a message that seems to give me the HWND for the views.

    I tried enumerating all the “scintilla” sub-windows of the main Notepad++ window… I found five listed; if I use the first one that has visibility turned on, I can get the HWND for (what seems to usually or maybe always be) the active scintilla. But how can I tell whether it’s for PRIMARY or SECONDARY, and how can I get the HWND for the other?

    I also tried looking through the PythonScript source code, but I cannot figure out where the editor1/editor2 come from – I found that importScintilla() sets them, and there’s a call to importScintilla(mp_scintilla, mp_scintilla1, mp_scintilla2), but I’ve gotten confused as to the call path earlier than that, and cannot figure out where mp_scintilla* come from.



  • Plugins have an exported setInfo() function that will receive a NppData struct. It is defined as:

    struct NppData
    {
    	HWND _nppHandle;
    	HWND _scintillaMainHandle;
    	HWND _scintillaSecondHandle;
    };
    




  • @dail said:

    Plugins have an exported setInfo() function

    Looks like that might be something I won’t have access to thru my external app. :-( Might have to rethink things…



  • @PeterJones

    You might get more ideas generated if you tell us more about this mysterious “external app”. ;)



  • @Alan-Kilborn said:

    You might get more ideas generated if you tell us more about this mysterious “external app”. ;)

    https://notepad-plus-plus.org/community/topic/17973/template-new/7

    I’ve always wanted a PerlScript plugin… and, barring that (since I don’t have the VS development environment, and doubt I’ll get one set up at home in the foreseeable future), as an alternative, I wanted to try to make the Perl modules necessary to use SendMessage() to interface with Notepad++ externally, with some or all of the same functions as available to PythonScript.

    The referenced thread got my mind working on this again. I made a major breakthrough recently in figuring out how to properly read back the LPARAM-based message-return values (though I’m still also struggling with the TCHAR** wparam values… but I can live without the few messages that use those, for now), so that encouraged me that it wasn’t a complete dead end, and I started working on it again.

    As I said in the OP, I can enumerate the various Scintilla HWNDs using general Win32 methodology for finding child-windows with certain attributes – but there are more than two Scintilla HWNDs (presumably because other plugins have created extra scintilla instances for their consoles and similar; oh, and probably the search results is another scintilla instance)… but maybe there’s a way through shooting messages to those HWNDs to determine whether they are one of the two main Notepad++ views (and if so, which one)? Or a way to correlate the scintilla HWNDs to some SendMessage-accessible information in Notepad++ itself.



  • @PeterJones

    In the plugin list of the old PluginManager there was a plugin called ActiveX plugin. This plugin exposes an ActiveX programming interface to Notepad++. You can download it here. I don’t know if you have access from Perl to ActiveX objects and I don’t know if the plugin still works with recent versions of Notepad++ (it works at least with v7.5.6).

    At plugin installation you will be asked to register the plugin’s ActiveX object. If you decline that, it only allows you to access the programming interface from within Notepad++ by using its menu entries Execute custom script and Execute current document. If you allow the registration, you can access the programming interface from every script language that supports ActiveX objects as well.

    The plugin exposes an INppApplication object which represents Notepad++. This object has an array property editors of type INppEditor which represent the two Scintilla views. These objects have the property hWnd which should be what you are looking for.

    Moreover, the plugin provides an abstraction for the most important operations on documents and their management. This will save you some coding effort.

    Example code in VBScript:

    Set objNppApplication = CreateObject("NotepadPlusPlus.Application")
    intScWnd0 = objNppApplication.editors(0).hWnd
    

    Good luck!



  • @PeterJones

    if call it from outside, meaning within process but not as a plugin you could enumerate the child windows like this

    import ctypes, ctypes.wintypes
    
    EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool,
                                         ctypes.wintypes.HWND,
                                         ctypes.wintypes.LPARAM)
    
    user32 = ctypes.WinDLL('user32', use_last_error=True)
    scintilla_hwnd = {0:None, 1:None}
        
    def find_scintilla_windows(npp_handle):
    
        def foreach_window(hwnd, lParam):
            curr_class = ctypes.create_unicode_buffer(256)
            user32.GetClassNameW(hwnd, curr_class, 256)
    
            if curr_class.value == 'Scintilla':
                if user32.GetParent(hwnd) == npp_handle:
                    if scintilla_hwnd[0] is None:
                        scintilla_hwnd[0] = hwnd
                    elif scintilla_hwnd[1] is None:
                        scintilla_hwnd[1] = hwnd
                        return False
            return True
    
        return not user32.EnumChildWindows(npp_handle,
                                           EnumWindowsProc(foreach_window),
                                           None)
    find_scintilla_windows(user32.FindWindowW(u'Notepad++', None))
    print scintilla_hwnd
    

    I assume that enumerating child windows follow the order of how those where created previously therefore it should be reliable.



  • @Ekopalypse said:

    I assume that enumerating child windows follow the order of how those where created previously therefore it should be reliable.

    Thanks. Your enumeration code looks very similar to what the Perl library I’m using does the enumeration under-the-hood. I guess I’ll run some experiments to see if the order is consistently the same, and consistently gets me access to the correct Scintillas.

    @dinkumoil suggested,

    This plugin exposes an ActiveX programming interface to Notepad++.

    Interesting idea. I’ve never tried to access ActiveX from Perl. If I find inconsistencies in the enumerated-scintilla order, I may have to look into that extra layer.

    I’ll report back when I’ve confirmed/contradicted the enumeration order, and on any other progress or ideas I have on the HWND side of things. There also may be other topics created as I run across other issues.

    Thanks for everybody’s input so far. Much appreciated.



  • @Ekopalypse

    Looks good; however, I would put a u in front of 'Scintilla'.

    :)



  • @PeterJones

    I’ve always wanted a PerlScript plugin

    Yeah, me too …

    There’s this: NPPRunPerl, “A Notepad++ plugin to comfortably create and run a perl script to transform the current file or selection”.

    I tried it in the past and it wasn’t quite as robust or reliable as I’d hoped.

    Cheers.



  • @PeterJones

    So it would be interesting to keep reading about your marriage-of-npp-and-perl saga here in this thread. Do keep us posted.

    I presume that once you have the scintilla_hwnds, you’ll proceed by creating a wrapper function for each of the Scintilla functions, like the Pythonscript Editor class provides in that language for the instantiated objects editor1 and editor2.

    Maybe something like this Python, but translated into Perl:

    sci_GetDirectPointer = 2185
    sci_GetMargins = 2253
    scintilla_direct_pointer = [ SendMessage(scintilla_hwnd[0], sci_GetDirectPointer, 0, 0), SendMessage(scintilla_hwnd[1], sci_GetDirectPointer, 0, 0) ]
    scintilla_direct_function = ctypes.WinDLL('SciLexer.dll', use_last_error=True).Scintilla_DirectFunction
    
    def editor_GetMargins(view): return scintilla_direct_function(scintilla_direct_pointer[view], sci_GetMargins, 0, 0)
    

    which leads into usage:

    print('view 0 number_of_margins:', editor_GetMargins(0))
    

    (I use the get-margins example function here because I have code like the above in use, because the Pythonscript API doesn’t have anything for it, or I can’t find it.)

    Anyway, definitely looking forward to reading more about the Perl thing here!



  • @Alan-Kilborn said:

    you’ll proceed by creating a wrapper function for each of the Scintilla functions, like the Pythonscript Editor class

    Yes, my goal would be to get as many of the Messages (Notepad++ or Scintilla) as I can. Though full implementation (or even matching all of PythonScript’s available messages) will take a while.

    once you have the scintilla_hwnds

    I’ve thought of a fallback, though it’s not elegant. I might be able to launch my ExternalPerlScript automator from Notepad++ (maybe through NppExec, or irony of ironies, through PythonScript), and pass along the scintilla HWNDs as arguments.
    Or, if I don’t need a dockable component, maybe @Michael-Vincent could get me to the point he mentioned here, with a gcc-compiled plugin, as long as it doesn’t need a dockable component. Because really, if I have a plugin that just responds to a couple of queries, I might be able to send NPPM_MSGTOPLUGIN from the external perl process to talk with the internal simple-plug.

    Hmm, how about it, @Michael-Vincent? Do you still have a super-simple plugin that just uses gcc/g++ and gmake/dmake to get an about-box? If I could start with that, I’d have a new direction to explore (and might eventually figure out how to get more fancy, and learn more about the plugins, even if I never go dockable…)



  • @PeterJones

    When I started the simple plugins I “wrote” (read: liberally borrowed others’ code and put into the provided N++ plugin template), I started with gcc / gmake (provided from Strawberry Perl). As that other thread indicates, once I started to add dockable components, it wouldn’t work. There were also some other issues in that I wasn’t statically linking the libc so if I compiled for 32-bit but then had my 64-bit Perl in my path, my plugin wouldn’t load in 32-bit N++.

    Anyway, to see examples of some simple plugin with a Makefile for gcc:

    https://github.com/vinsworldcom/nppGitSCM/releases/tag/1.0.1.1
    https://github.com/vinsworldcom/nppColumnTools/releases/tag/1.0.1.2



  • @PeterJones said:

    I’ll run some experiments to see if the order is consistently the same

    So far, so good.

    At first, I was seeing the Find Results scintilla window (and PythonScript scintilla window) show up in my enumeration before the first, but when I’d run @Ekopalypse’s python-based enumeration, it always only listed the four, and the first two always matched the two editors. I eventually determined that the Find Results and Python Script have an intermediate generation between the NPP hwnd and the scintilla hwnd; Eko’s python enumeration correctly checked whether their immediate parent was NPP, and ignored it if not; once I added that to my enumeration, it seems to keep the two editors as the first two.

    I’ll probably make that assumption for now, and move forward.

    Again, thank you all.



  • @Alan-Kilborn

    Looks good; however, I would put a u in front of ‘Scintilla’.

    Ahh, python2 :-D can handle this :-D

    @PeterJones
    I don’t have access to my windows pc at the moment, but I played a little bit trying to embed perl in a c++ program. Seems to be pretty straightforward.
    On linux afer installing libperl-dev this compiled.

    #include <EXTERN.h>
    #include <perl.h>
    
    static PerlInterpreter *my_perl;
    
    
    void run(char ** filename)
    {
        my_perl = perl_alloc();
        perl_construct(my_perl);
        perl_parse(my_perl, NULL, 0, filename, (char **)NULL);
        perl_run(my_perl);
        perl_destruct(my_perl);
        perl_free(my_perl);
    
    }
    
    
    int main(int argc, char **argv, char **env)
    {
        char* filename[] = {"", "/home/eko/Documents/c_c++/test.pl"};
        run(filename);
    }
    

    Next week when I’m at home again, I could try to see if this can be embedded into a win dll if no one else found/reported how to do it in the meantime.



  • UPDATE: repo for the perl module is now publicly available at https://github.com/pryrt/Win32-Mechanize-NotepadPlusPlus

    So far:

    • I can get the HWND for the Notepad++ GUI as well as the first two child-Scintilla windows, which are assumed to be editor1 and editor2
    • I can send generic messages to the Notepad++ object. There are wrappers for grabbing an integer or a single string from the LPARAM. Still need to work on other LPARAM and/or WPARAM return-data-types
    • Still need to write all the Perl wrappers for Notepad++ and Scintilla messages (ie, the bulk of it)

    Once I got it to the point that I had the editor HWNDs, and was able to convert the NPP and Scintilla messages from the .h files to Perl sub-modules (to give Perl easy access to a hash of messages for each window type), I decided it was sufficient to move from my private subversion repo to a public GitHub repo.

    It shows the general structure, and the whole look-and-feel of my coding, though there’s not much there.



  • @PeterJones @michael-vincent

    I need some advice.
    I cloned the perl github repo and built, tested and installed perl - so far so good, got some interpreter which seems to do what it should.
    Now, when trying to build an embedded perl with the same, except for the path of the test script, c++ code as posted before it crashes when doing perl_parse.
    It looks like the issue is that it is looking for a path or perl module called MSWin32-x64-multi-thread like
    …EmbeddedPerl\x64\Debug\lib\MSWin32-x64-multi-thread.
    Searching the web seems to indicate that MSWin32-x64-multi-thread is only the constructed architecture name used to build unique
    interpreters but not that there is something created in .\lib directory called MSWin32-x64-multi-thread. Did you faced some similar issue
    when working with perl? Do you have some good mailing lists, forums etc… where I could ask for clarification about this?

    Thank you



  • I cloned the perl github repo and built, tested and installed perl

    On Windows? Wow, that’s impressive. I always use Strawberry Perl. (But from what little I know of embedding Perl in other apps/libraries/etc, I think you need to use the same compiler – or at least compatible compiler options – so it might be a necessary evil.)

    Do you have some good mailing lists, forums etc… where I could ask for clarification about this?

    Michael and I are both on perlmonks.org (he’s vinsworldcom, I’m pryrt), and that’s where I’d recommend asking.



  • @PeterJones

    that’s where I’d recommend asking.

    Thanks, and that’s what I did :-)


Log in to reply