Aufhänger bei _sendMessage

  • Hallo liebe Community,

    Ich versuche mich an Interprozesskommunikation und schicke dem TopWindow meines Scriptes eine WindowMessage.
    Sobald ich im Skript eine GUI erstelle hängt es sich aber ohne Meldung auf. Warum?

    [autoit]


    #Include <Constants.au3>
    #Include <WinAPI.au3>
    #Include <WindowsConstants.au3>

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

    $hDateGuiWrapper = GUICreate( "" )
    ;~ GUISetState() ; SOBALD DAS AUSKOMMENTIERT WIRD HÄNGT SICH DAS SKRIPT AUF OHNE FEHLERMELDUNG !!!!!

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

    $hHook = DllCallbackRegister( '_WinProc' , 'ptr' , 'hwnd;uint;long;str' ) ; 1 - Callback vorbereiten:
    $pHook = DllCallbackGetPtr( $hHook )

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

    $autoItWin = WinGetHandle( AutoItWinGetTitle() )
    $hProc = _WinAPI_SetWindowLong( $autoItWin , $GWL_WNDPROC, $pHook ) ; 2 - Window Proc austauschen

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

    $WM_TEST = _WinAPI_RegisterWindowMessage( "testMessage" ) ; 3 - eigene Window-Message-Art erstellen:

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

    _SendMessage( $autoItWin , $WM_TEST , 55 , "Test" , 0 , "wparam" , "str" ) ; 4 - Testnachricht schicken

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

    Func _WinProc( $hWnd , $iMsg , $wParam , $lParam )

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

    Local $Res = _WinAPI_CallWindowProc( $hProc, $hWnd, $iMsg, $wParam, $lParam )

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

    if $wParam == 55 Then
    Beep( 500, 200 )
    EndIf

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

    Return $Res

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

    EndFunc

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

    While 1
    WEnd

    [/autoit]


    Gruß

    Blues

    2 Mal editiert, zuletzt von Bluesmaster (5. November 2013 um 08:41)

    • Offizieller Beitrag

    Funktioniert tadellos, wenn du deine Programmschleife mal aus der Dauerlast nimmst und Sleep oder GuiGetMsg einbaust. ;)

    Spoiler anzeigen
    [autoit]

    #Include <Constants.au3>
    #Include <WinAPI.au3>
    #Include <WindowsConstants.au3>

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

    $hDateGuiWrapper = GUICreate( "" )
    GUISetState() ; SOBALD DAS AUSKOMMENTIERT WIRD HÄNGT SICH DAS SKRIPT AUF OHNE FEHLERMELDUNG !!!!!

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

    $hHook = DllCallbackRegister( '_WinProc' , 'ptr' , 'hwnd;uint;long;str' ) ; 1 - Callback vorbereiten:
    $pHook = DllCallbackGetPtr( $hHook )

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

    $autoItWin = WinGetHandle( AutoItWinGetTitle() )
    $hProc = _WinAPI_SetWindowLong( $autoItWin , $GWL_WNDPROC, $pHook ) ; 2 - Window Proc austauschen

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

    $WM_TEST = _WinAPI_RegisterWindowMessage( "testMessage" ) ; 3 - eigene Window-Message-Art erstellen:

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

    _SendMessage( $autoItWin , $WM_TEST , 55 , "Test" , 0 , "wparam" , "str" ) ; 4 - Testnachricht schicken

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

    While 1
    Switch GUIGetMsg()
    Case -3
    Exit
    EndSwitch
    WEnd

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

    Func _WinProc( $hWnd , $iMsg , $wParam , $lParam )
    Local $Res = _WinAPI_CallWindowProc( $hProc, $hWnd, $iMsg, $wParam, $lParam )
    if $wParam == 55 Then
    ;~ Beep( 500, 200 )
    ConsoleWrite('BLA' & @LF)
    EndIf
    Return $Res
    EndFunc

    [/autoit]


    NB: "==" ist für sensitiven Vergleich bei Strings, für alle anderen Vergleiche reicht das "=".

  • Bei mir leider nicht. Erst ab sleep( 3000) stürzte es nicht mehr ab, und nur wenn ich nicht sofort das Fenster wechsle (das löst ein paar Broadcast Messages aus)

    Das Problem ist also, das meine WindowProc zu langsam reagiert oder was ist die technische Ursache für die Abstürze?

    Gruß

    Blues

    • Offizieller Beitrag

    Also ich kann einen Crash nur reproduzieren, wenn die Mainloop ohne jede Unterbrechung läuft.
    Ich arbeite viel mit Message-Hooks und hatte bisher noch nie einen permanenten Fehler. AutoIt ist nicht optimal im Umgang mit Callbacks, aber trotzdem sind Crashs recht selten.
    Ich habe z.B. ein Kontextmenü-Ctrl darauf basierend erstellt und das habe ich noch nie gecrasht.

  • Ich kann die Crashs auch nicht 100% reproduzieren, sie tauchen auch nur beim ToplevelWindow des Scripts auf.
    Bei "GUIRegisterMsg" mit eigener GUI kann ich problemlos Messages empfangen. Das hingegen crasht trotz sleep:

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

    #Include <Constants.au3>
    #Include <WinAPI.au3>

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

    $pHook = DllCallbackGetPtr( DllCallbackRegister( '_WinProc' , 'ptr' , 'hwnd;uint;long;str' ) )
    $autoItWin = WinGetHandle( AutoItWinGetTitle() )
    _WinAPI_SetWindowLong( $autoItWin , $GWL_WNDPROC, $pHook ) ; replace winProc

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

    GUICreate( "" )
    GUISetState()

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

    While 1
    Sleep( 500 )
    WEnd

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

    Func _WinProc( $hWnd , $iMsg , $wParam , $lParam )
    EndFunc

    [/autoit]

    Win8 x64. Ich werde jetzt eben eine unsichtbare GUI verwenden, aber ich hätte so gern die Hintergründe verstanden

    Gruß

    Blues

    • Offizieller Beitrag

    Nun, das kann natürlich nicht funktionieren, da du den Hook auf einem nicht existierenden Fensetr aufsetzt!

    [autoit]

    $autoItWin = WinGetHandle( AutoItWinGetTitle() )

    [/autoit]

    Das gibt es gar nicht zu diesem Zeitpunkt.Verwende einfach mal:

    [autoit]

    $autoItWin = WinGetHandle( WinGetTitle('[ACTIVE]') )

    [/autoit]

    , dann siehst du, dass es funktioniert. ;)

  • könntest du mir noch eine kleine Frage beantworten die mich gerade 1 h den letzten Nerv gekostet hat:
    Wie versende ich einen String als Window Message? Dazu gibt es meterlange Foren meist ohne Ergebniss

    [autoit]

    #Include <Constants.au3>
    #Include <WinAPI.au3>

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

    $WM_IPCMESSAGE = _WinAPI_RegisterWindowMessage( "IPCMESSAGE" ) ; eignen Nachrichtentyp erstellen

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

    $ipcGUI = GUICreate( "Test" )
    GUIRegisterMsg( $WM_IPCMESSAGE , '_DispatchIPCMessage' )
    GUISetState()

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

    _SendMessage( $ipcGUI , $WM_IPCMESSAGE , 55 , "TestL" , 0 , "str" , "str" ) ; Testnachricht
    ;~ $result = DllCall( "user32.dll" , "int" , "SendMessage" , "hWnd" , $ipcGUI ,"int" , $WM_IPCMESSAGE , "int" , "1" , "str" , "test" )

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

    Func _DispatchIPCMessage( $hWnd , $iMsg , $wParam , $lParam )

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

    ;~ $lParamConvertedToString = magicalConversionToString( $lParam ) ;;;WIE KONVERTIEREN ????
    ;~ ToolTip( $lParamConvertedToString )
    ToolTip( $lParam ) ; scheint ein Pointer zu sein

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

    EndFunc

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

    sleep( 3000 )

    [/autoit]

    Danke vielmals

    • Offizieller Beitrag

    Wie versende ich einen String als Window Message?


    Ich habs bisher nicht gebraucht, würde aber versuchen den String in eine Struktur zu packen (0-terminiert) und dann versenden. Dann kannst du den empfangenen Pointer sofort in eine identische Struktur entpacken und hast den Wert.

  • Guten Morgen,

    Genau daran habe ich mir ja so die Zähne ausgebissen (kaum Erfahrung mit DLLs).
    Erstaunlich ist, dass die Lösung mit ausgetauschter WinProc problemlos Strings akzeptiert ohne Kovertierungen (siehe 1. Post)

    Da es anscheinend irgendwie möglich ist, würde ich lieber ohne DLL-Structs arbeiten, da die Senderseite möglichst einfach sein soll.
    Ich bräuchte ein winziges Snippet, aber vielleicht stelle ich später eine neue Anfrage, da es nicht mehr wirklich zum Thema gehört.

    Dankeschön

    • Offizieller Beitrag

    Hi,

    ist noch einfacher als ich dachte:

    [autoit]


    #Include <Constants.au3>
    #Include <WinAPI.au3>

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

    $WM_MEINE_MESSAGE = _WinAPI_RegisterWindowMessage("MEINE_MESSAGE")

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

    $hGUI = GUICreate("Test")
    GUISetState()

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

    GUIRegisterMsg($WM_MEINE_MESSAGE , '_WM_MEINE_MESSAGE')

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

    _SendString($hGUI, $WM_MEINE_MESSAGE, 'Test-String')

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

    Func _SendString($hWnd, $iMsg, $sString)
    _SendMessage($hWnd, $iMsg, StringLen($sString), $sString, 0, "wparam", "str")
    ; == als "wParam" übergibst du die Stringlänge, "lParam" wird nicht verwendet sondern "str"
    ; == In der Messagefunktion wird der String als Pointer empfangen
    ; == Mit Hilfe der übergebenen Stringlänge wird der Pointer in eine Struktur entladen und ausgelesen
    EndFunc

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

    Func _WM_MEINE_MESSAGE( $hWnd , $iMsg , $wParam , $lParam)
    Local $sLen = BitAND($wParam, 0xFFFF) ; Low Word
    Local $tMsg = DllStructCreate('char[' & $sLen & ']', $lParam)

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

    ConsoleWrite(DllStructGetData($tMsg, 1) & @LF)
    EndFunc

    [/autoit]

    Edit:
    Wenn du mit deiner eigenen Message unterschiedliche Varianten senden möchtest (also eventuell mal String, mal Struktur), kannst du dazu auch einfach "wparam" nutzen. Du setzt dann z.B. im Lo-Byte die Länge und im Hi-Byte "-1" als Trigger dass ein String übergeben wurde.
    Hier mal im Bsp. beide Versionen

    Spoiler anzeigen
    [autoit]

    #Include <Constants.au3>
    #Include <WinAPI.au3>

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

    Global $tag_MY_SENDSTRUCT = "int size;char timestring[8];char datestring[10];" ; == eigene Struktur-Konstante

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

    $WM_MEINE_MESSAGE = _WinAPI_RegisterWindowMessage("MEINE_MESSAGE")

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

    $hGUI = GUICreate("Test")
    GUISetState()

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

    GUIRegisterMsg($WM_MEINE_MESSAGE , '_WM_MEINE_MESSAGE')

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

    ; == Text senden
    _SendMyMessage($hGUI, $WM_MEINE_MESSAGE, 'Test-String')

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

    ; == Struktur senden
    $tSEND = DllStructCreate($tag_MY_SENDSTRUCT)
    $pSEND = DllStructGetPtr($tSEND)
    DllStructSetData($tSEND, 1, DllStructGetSize($tSEND))
    DllStructSetData($tSEND, 2, @HOUR & ':' & @MIN & ':' & @SEC)
    DllStructSetData($tSEND, 3, @YEAR & '/' & @MON & '/' & @MDAY)
    _SendMyMessage($hGUI, $WM_MEINE_MESSAGE, $pSEND, 0)

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

    Func _SendMyMessage($hWnd, $iMsg, $lParam, $wParamLo=-1, $wParamHi=0)
    Local $s_lParam = 'lparam'
    If $wParamLo = -1 Then ; == lParam ist String
    $wParamLo = StringLen($lParam)
    $wParamHi = -1
    $s_lParam = 'str'
    EndIf
    _SendMessage($hWnd, $iMsg, _WinAPI_MakeLong($wParamLo, $wParamHi), $lParam, 0, "wparam", $s_lParam)
    EndFunc

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

    Func _WM_MEINE_MESSAGE( $hWnd , $iMsg , $wParam , $lParam)
    Local $iLo = BitAND($wParam, 0xFFFF)
    Local $iHi = BitShift($wParam, 16)
    If $iHi = -1 And $iLo > 0 Then ; == String
    Local $sLen = $iLo
    Local $tMsg = DllStructCreate('char[' & $sLen & ']', $lParam)
    ConsoleWrite(DllStructGetData($tMsg, 1) & @LF)
    Else
    Local $tRECEIVE = DllStructCreate($tag_MY_SENDSTRUCT, $lParam)
    ConsoleWrite('Size' & @TAB & DllStructGetData($tRECEIVE, 1) & @LF)
    ConsoleWrite('Time' & @TAB & DllStructGetData($tRECEIVE, 2) & @LF)
    ConsoleWrite('Date' & @TAB & DllStructGetData($tRECEIVE, 3) & @LF)
    EndIf
    EndFunc

    [/autoit]
  • Hervorragend. Vielen herzlichen Dank. Frage beantwortet.

    Leider bekomme ich keine Emails vom Forum, sodass ich immer erst durch "Zufall" auf Antworten stoße,
    werde das nochmal nachfragen obwohl ich schon 2x gefragt hatte.

    Übrigens:
    $autoItWin = WinGetHandle( AutoItWinGetTitle() )
    ...war schon korrekt. Das ist doch das versteckte Fenster, das zu jedem skript gehört.
    Ich wollte einfach die Extra-GUI sparen.
    Zudem lässt sich der String so ohne Konvertierungen versenden, wodurch man sich das
    senden der Stringlänge spart.

    Gruß

    Blues

    Einmal editiert, zuletzt von Bluesmaster (2. November 2013 um 18:45)

  • Hallo,

    Tut mir total leid, aber ich muss den Thread nochmal aufgreifen, denn wir haben ein entscheidendes Detail übersehen:

    Der Sender soll eigentlich ein fremder Prozess sein (es geht ja um Interprozesskommunikation).
    Der gesendete Pointer ist dann aber natürlich ungültig und der Empfängerprozess schmiert ab.

    EMPFÄNGER:

    [autoit]

    #Include <Constants.au3>
    #Include <WinAPI.au3>

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

    $WM_MEINE_MESSAGE = _WinAPI_RegisterWindowMessage("MEINE_MESSAGE")
    $hGUI = GUICreate("TestGUI")
    GUIRegisterMsg($WM_MEINE_MESSAGE , '_WM_MEINE_MESSAGE')

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

    Func _WM_MEINE_MESSAGE( $hWnd , $iMsg , $wParam , $lParam)

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

    Local $sLen = BitAND($wParam, 0xFFFF) ; Low Word
    Local $tMsg = DllStructCreate('char[' & $sLen & ']', $lParam)
    ConsoleWrite( 'msg ' & DllStructGetData( $tMsg, 1) & @LF )

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

    EndFunc

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

    $sString = 'Test'
    _SendMessage( $hGUI , $WM_MEINE_MESSAGE , StringLen($sString), $sString, 0, "wparam", "str")

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

    While 1
    Sleep(1000)
    WEnd

    [/autoit]

    SENDER:

    [autoit]

    #Include <Constants.au3>
    #Include <WinAPI.au3>

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

    $WM_MEINE_MESSAGE = _WinAPI_RegisterWindowMessage( "MEINE_MESSAGE" )

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

    $sString = 'Test'
    _SendMessage( WinGetHandle( "TestGUI" ) , $WM_MEINE_MESSAGE , StringLen($sString), $sString, 0, "wparam", "str" )

    [/autoit]


    Jetzt bin ich leider völlig ratlos. Sollte ich aus dem Speicher des anderen Prozesses lesen? Oder gibt es noch eine einfacherer Möglichkeit?

    Gruß

    Blues