Interaktion mit Notepad++

    • Offizieller Beitrag

    Hi,
    ich bastel ja gerade an der Verwendung von Notepad++ als AutoIt-Editor.
    Nun benötige ich Interaktionen mit dem NPP-Fenster (Scintilla-Edit-Control). Scintilla verwendet einen eigenen Adressraum, somit kann man nicht mit WM_GETTEXT und WM_SETTEXT arbeiten. Dazu gibt es von Scintilla eigene Messages.
    Ich habe jetzt mal ein Skript nachgestellt um den Text des aktuell offenen NPP-Fensters auszugeben. Aber schon die Abfrage der Textlänge bleibt ohne Erfolg.

    [autoit]

    #Include <SendMessage.au3>

    [/autoit][autoit][/autoit][autoit]

    Local $Wnd = WinGetHandle('[CLASS:Notepad++]'), $Len
    $Len = _SendMessage($Wnd, 2183, 0, 0) ; Rückgabe '0', kein Fehler

    [/autoit]


    Hat jemand eine Idee?

    • Offizieller Beitrag

    Also das Abfragen des Scintilla Handle führt unweigerlich zum Crash von Notepad++. ;(
    Andere Aktionen, die das Scintilla-Handle nicht benötigen, funktionieren problemlos:

    Spoiler anzeigen
    [autoit]

    #Include <SendMessage.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Local Const $WM_USER = 0x400
    Local Const $NPPMSG = $WM_USER + 1000

    [/autoit] [autoit][/autoit] [autoit]

    Local Const $NPPM_GETCURRENTSCINTILLA = ($NPPMSG + 4)
    Local Const $NPPM_GETCURRENTDOCINDEX = ($NPPMSG + 23)
    Local Const $MAIN_VIEW = 0
    Local Const $SUB_VIEW = 1
    Local Const $NPPM_GETMENUHANDLE = ($NPPMSG + 25)
    Local Const $NPPM_ACTIVATEDOC = ($NPPMSG + 28)
    ;~ void $NPPM_ACTIVATEDOC(int view, int index2Activate)
    Local Const $NPPM_GETWINDOWSVERSION = ($NPPMSG + 42)
    ;~ winVer $NPPM_GETWINDOWSVERSION(0, 0)

    [/autoit] [autoit][/autoit] [autoit]

    Local $hNpp = WinGetHandle('[CLASS:Notepad++]')

    [/autoit] [autoit][/autoit] [autoit]

    ; Index (0-basiert des aktuellen Dokuments) ==> funktioniert
    Local $iDocIndex = _SendMessage($hNpp, $NPPM_GETCURRENTDOCINDEX)
    ConsoleWrite('@@ DEBUG_Line: ' & @ScriptLineNumber & ', Var: $iDocIndex >> ' & $iDocIndex & ' ; @error: ' & @error & ', @extended: ' & @extended & @LF) ; Debug To Console

    [/autoit] [autoit][/autoit] [autoit]

    ; Dokument vor dem aktuellen aktivieren ==> funktioniert
    Local $ret = _SendMessage($hNpp, $NPPM_ACTIVATEDOC, $MAIN_VIEW, $iDocIndex-1)
    ConsoleWrite('@@ DEBUG_Line: ' & @ScriptLineNumber & ', Var: $ret >> ' & $ret & ' ; @error: ' & @error & ', @extended: ' & @extended & @LF) ; Debug To Console

    [/autoit] [autoit][/autoit] [autoit]

    ; Menü-Handle abfragen ==> funktioniert
    $hMenu = _SendMessage($hNpp, $NPPM_GETMENUHANDLE)
    ConsoleWrite('@@ DEBUG_Line: ' & @ScriptLineNumber & ', Var: $hMenu >> ' & $hMenu & ' ; @error: ' & @error & ', @extended: ' & @extended & @LF) ; Debug To Console

    [/autoit] [autoit][/autoit] [autoit]

    ;=================================================
    ; Scintilla-Handle abfragen ==> Exception Error !
    Local $hScintilla = _SendMessage($hNpp, $NPPM_GETCURRENTSCINTILLA)
    ConsoleWrite('@@ DEBUG_Line: ' & @ScriptLineNumber & ', Var: $hScintilla >> ' & $hScintilla & ' ; @error: ' & @error & ', @extended: ' & @extended & @LF) ; Debug To Console

    [/autoit]


    Ich bekomme einen Exception Error und NPP bietet dann an, einen Dump zu speichern, was dann im Crash endet mit unzähligen Fenstern "Visual C++ Runtime Error".

    autoit.de/wcf/attachment/12889/autoit.de/wcf/attachment/12890/

    • Offizieller Beitrag

    Nach dem Lesen von unzähligen Dokus und Quelltexten bin ich auf die "PluginManager.dll" gestoßen. Diese enthält eine Funktion "setInfo" und bekommt als Parameter eine Struktur (NppData) mit 3 Handle übergeben.
    Das sieht dann so aus:

    Spoiler anzeigen
    [autoit]

    Local $tNppData = DllStructCreate( _
    "HWND _nppHandle;" & _
    "HWND _scintillaMainHandle;" & _
    "HWND _scintillaSecondHandle;")

    [/autoit] [autoit][/autoit] [autoit]

    Local $hDll = DllOpen(@ProgramFilesDir & "\notepad++\plugins\pluginmanager.dll")
    If @error Then
    Exit ConsoleWrite('DllOpen Error: ' & @error & @LF)
    Else
    ConsoleWrite('DllOpen: OK' & @LF)
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Local $ret = DllCall($hDll, 'int:cdecl', 'setInfo', 'ptr', DllStructGetPtr($tNppData))
    If @error Then Exit ConsoleWrite('DllCall Error: ' & @error & @LF)
    DllClose($hDll)

    [/autoit] [autoit][/autoit] [autoit]

    ConsoleWrite('_nppHandle: ' & DllStructGetData($tNppData, 1) & @LF)
    ConsoleWrite('_scintillaMainHandle: ' & DllStructGetData($tNppData, 2) & @LF)
    ConsoleWrite('_scintillaSecondHandle: ' & DllStructGetData($tNppData, 3) & @LF)

    [/autoit]

    Jedoch wird bei meinem Aufruf die Struktur nicht befüllt. Die Source für die pluginmanager.dll habe ich nicht finden können. Habe testweise statt 'ptr' mal 'int_ptr' verwendet, aber auch ohne Erfolg.
    Also der Dll-Call ist erfolgreich, kein Fehler und ein Return > 0.
    Nun hatte ich die Struktur als Fehlerquelle überprüft und dort mal "HWND" mit "HANDLE" ersetzt, auch ohne Erfolg.
    Im Moment gehen mir da die Ideen aus. :wacko:

  • Direkt mit AutoIt funktioniert das nicht. Es muss eine DLL erstellt werden, die NP++ als Plugin einbindet. setInfo wird dann von NP++ beim Laden der DLL aufgerufen.

    • Offizieller Beitrag

    Danke progandy, dann werde ich mal probieren das hinzubekommen.
    Vielleicht kann ich ja einfach ein vorhandenes Plugin dazu umfunktionieren.
    Ich melde mich hier, wenn ich ein Ergebnis habe. (Kann dauern, noch habe ich Null-Ahnung von C++ und Dll-Erstellung ;))

  • Ich würde dir vorschlagen ein "NPppDirector"-Plugin zu schreiben. Das empfängt alle Daten von NP++ und macht sonst nichts.
    Anschließend wird ein Fenster mit bestimmtem Namen erstellt. Dieses reagiert dann auf benutzerdefinierte Nachrichten und sendet dann die gewünschten Handles als Rückgabewert.

    Das sollte auch mit FreePascal oder FreeBasic machbar sein.
    Edit: Ich versuch mich gerade dran.
    Edit: Ich hab mal was zusammengebastelt in FreePascal. Der Code ist nicht schön, aber es läuft:

    Spoiler anzeigen
    [autoit]

    #include<WindowsConstants.au3>
    #include<SendMessage.au3>
    Global Const $NDM_GETSCINTILLAMAIN = $WM_USER+101;
    Global Const $NDM_GETSCINTILLASECOND = $WM_USER+102;
    Global Const $NDM_GETNPPWINDOW = $WM_USER+103;
    Func _Npp_GetDirector()
    Return WinGetHandle("[CLASS:NppDirectorWindow; TITLE:NppDirectorWindow]")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Npp_GetNppWindow($hDirector)
    Return _SendMessage($hDirector, $NDM_GETNPPWINDOW, 0, 0, 0, "wparam", "lparam", "hwnd")
    EndFunc
    Func _Npp_GetScintillaMain($hDirector)
    Return _SendMessage($hDirector, $NDM_GETSCINTILLAMAIN, 0, 0, 0, "wparam", "lparam", "hwnd")
    EndFunc
    Func _Npp_GetScintillaSecond($hDirector)
    Return _SendMessage($hDirector, $NDM_GETSCINTILLASECOND, 0, 0, 0, "wparam", "lparam", "hwnd")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    $hDirector = _Npp_GetDirector()
    MsgBox(0, 'NPP', _Npp_GetNppWindow($hDirector))
    MsgBox(0, 'Scintilla Main', _Npp_GetScintillaMain($hDirector))
    MsgBox(0, 'Scintilla Second', _Npp_GetScintillaSecond($hDirector))

    [/autoit]
    • Offizieller Beitrag

    progandy, du bist ein Schatz :love:
    Ich habe bei meinen Versuchenschon graue Haare bekommen. Jetzt kann ich endlich weiterbasteln. :thumbup:

    Edit: Hmm, ich bekomme keine Handle, alles 0x00000000 8|

    Edit2: Das DirectorWindow-Handle wird zurückgegeben, nur die anderen nicht.

    Ich hab das mal mit DependencyWalker angeschaut. 2 Dateien scheinen problematisch zu sein.Ist das die Ursache?

    autoit.de/wcf/attachment/12898/

  • Keine Ahnung, was da los ist. Irgendwie mag NP++ mein Plugin nicht :(
    Edit: Ich vermute, dass das mit dem Fenster zusammenhängt, dass das irgendwie die Nachrichten nicht bekommt.

    Die beiden Abhängigkeiten sollten egal sein und werden auch nicht durch die DLL verursacht, sondern von user32.dll. Daher müsste das normal so sein.

    Einmal editiert, zuletzt von progandy (18. März 2011 um 20:47)

    • Offizieller Beitrag

    Ich habe nun mal versucht, auf Basis des DemoPlugins ein Kommunikations-Plugin zu erstellen.
    Probleme entstehen bei der MSG-Auswertung.
    Wenn ich die MSG, die ich ja aus AutoIt heraus mit _SendMessage() an dieses Fenster senden möchte, im dortigen Auswertezweig als "Case MSG" unterbringe, bekomme ich Fehlermeldungen (Zeile 71).
    Kann ich dann eigentlich die Handle z.B. mit "return NppData._scintillaMainHandle;" zurückgeben, oder ist noch mehr zu beachten?

    Spoiler anzeigen
  • Ein define hat kein Gleichheitszeichen;
    #define SUCHE ERSETZE

    Das Handle solltest du sofort zurückgeben können.

    • Offizieller Beitrag

    So, nun ist alles fehlefrei kompiliert, PlugIn zu Notepad++ rübergeschoben und NPP gestartet: "Dies ist ein ANSI-Plugin und kann nicht mit dieser Version von Notepad++ genutzt werden...."
    ?( Mist, wo zum Teufel wird das eingestellt? Sollte doch wohl irgendwo in den Build-Einstellungen sein. Aber ich finde nix. Ist es eine zusätzliche Angabe für den Linker, in der Art "UNICODE=1" ? Getestet hab ich das so, wurde aber von CodeBlocks abgelehnt.

  • Du musst die Funktion isUnicode exportieren. Und natürlich daraf achten, dass du die Buildoptionen so eingestellt hast, dass TCHAR in unicode char umgewandelt wird und nicht in ANSI.

    • Offizieller Beitrag

    Du musst die Funktion isUnicode exportieren

    Das hatte ich bereits drin:

    Code
    #ifdef UNICODE
    extern "C" __declspec(dllexport) BOOL isUnicode();
    #endif //UNICODE

    dass du die Buildoptionen so eingestellt hast, dass TCHAR in unicode char umgewandelt wird

    Und hier hakt es. Ich habe in den CodeBlocks Einstellungen gewühlt und das Manual vor und zurück gelesen. Ich finde einfach nicht diese Option. Ich weiß, dass ich mal zum Augenarzt muß - aber so blind bin ich doch wohl noch nicht, dass ich das übersehe - oder doch? :wacko:

  • Du musst _UNICODE und UNICODE definieren. Wenn du es nicht in den Optionen schaffst, dann mach eben in main.h 2x #define

    In den Optionen müsstest du irgendwo auch zusätzliche defines für den Compiler angeben können, hab gerade kein C::B da.
    Edit: Build-Options - Compiler - #defines

    Einmal editiert, zuletzt von progandy (19. März 2011 um 23:35)

    • Offizieller Beitrag

    Ahh, OK - habe jetzt einfach mal bei den Buildoptions in den Tab "#define' UNICODE mit eingetragen. Und siehe da es tut sich was: Mein Code fliegt mir um die Ohren! Unzählige Fehlermeldungen "...cannot convert.." :wacko:
    Man oh man, gar nicht so einfach mit dem C/C++ Krams.

  • Hmm, ja. Jeden String, den du schreinbst, musst du auf eine der folgenden Arten angeben:
    variable = L"Dein String"; (string direkt als Unicode) oder
    variable = _T("DeinString"); (String abhängig von #define UNICODE)

    • Offizieller Beitrag

    :wacko: Was sind das für Funktionen ( L, _T ) ? Ich habe eine 700-Seiten dicke C/C++ Referenz, aber das konnte ich da unter dem Funktionsverzeichnis nicht finden.
    In meiner Fensterdeklaration habe ich das jetzt verwendet.
    vorher:

    Code
    ...
        wc.lpszClassName =  lpszAppName;
        wc.lpszMenuName  =  lpszAppName;

    jetzt:

    Code
    wc.lpszClassName =  L(lpszAppName);
        wc.lpszMenuName  =  L(lpszAppName);

    Das Seltsame: jetzt bekomme ich nur für die erste der beiden Zeilen einen Error ( 'L' was not declared in this scope ). Müßte doch eigentlich 2-mal auftauchen. In welchem Include ist das eigentlich enthalten?

    Ach und hier

    Code
    #ifdef UNICODE
    extern "C" __declspec(dllexport) BOOL isUnicode();
    #endif

    bekomme ich jetzt folgenden Fehler: error: 'BOOL' does not name a type

  • Das musst du bei der Definition des Strings machen. Eventuell noch #include "tchar.h"
    ( Für automatische Wahl von Unicode bzw. ANSI je nach #define UNICODE )
    LPCTSTR lpszAppName = _T("NppWatcher");
    LPCTSTR lpszTitle = _T("Npp-PlugIn");
    bzw (immer Unicode):
    LPCWSTR lpszAppName = L"NppWatcher";
    LPCWSTR lpszTitle = L"Npp-PlugIn";

    PS: _T ist ein Makro, das für Unicode so defeniert ist, dass die Version L"Parameter" draus wird. Bei Ansi bleibt der String so stehen wie er ist. L ist ein Sprachkonstrukt, das einen Unicode-String anzeigt.

    Edit: L benötigt kein Include, das ist wie auch wchar_t bestandteil von c++.

    2 Mal editiert, zuletzt von progandy (20. März 2011 um 00:28)