Seite 1 von 1

Programmkommunikation ohne Netzwerk

Verfasst: 20.05.2007 07:23
von ts-soft
Hier gehts um Kommunikation von 2 Programmen auf dem selben Rechner,
über RegisterWindowMessage und #WM_CopyData.

Ich hab das jetzt in Client und Server aufgeteilt in dem Beispiel, das ist aber
nicht notwendig, beide können Daten senden und empfangen, nur eben die
Callbacks erweitern.

Ich hoffe mal, die Kommentare sind ausreichend :wink:
Der "Sender"

Code: Alles auswählen

; German forum: http://www.purebasic.fr/german/viewtopic.php?p=152509#152509
; Author: ts-soft
; Date: 20. May 2007
; OS: Windows
; Demo: No

Enumeration ; PrivatMessages
  #GetHwnd
  #SendHwnd
  #SomeValue
  #CloseMessage
  ; ...
EndEnumeration

Enumeration ; Gadgets
  #lblText
  #txtText
  #btnText
  #lblValue
  #txtValue
  #btnValue
EndEnumeration

; wir registrieren unsere Message
Global MyMessage.l = RegisterWindowMessage_("MyOwnMessage")
Global hWndClient.l

Procedure MyWindowCallback(WindowID, Message, wParam, lParam)
  Protected Result.l = #PB_ProcessPureBasicEvents
  Select Message
    Case MyMessage
      Select wParam
        Case #SendHwnd ; der Client sende sein Fensterhandle
          hWndClient = lParam
          DisableGadget(#btnText, #False)
          DisableGadget(#btnValue, #False)
          SetWindowTitle(0, "Sender")
       EndSelect
  EndSelect
  ProcedureReturn Result
EndProcedure

Procedure SendData(Message.s)
  Protected cd.COPYDATASTRUCT
  cd\dwData = 0 ; hier könnte man versch. Werte für versch. Datentypen einsetzen
  cd\cbData = (Len(Message) + 1) * SizeOf(Character); wir verwenden nur Strings und geben hier die länge an
  cd\lpData = @Message ; databuffer ; der string in die Struktur
  SendMessage_(hWndClient, #WM_COPYDATA ,WindowID(0), cd) ; und raus damit ;)
EndProcedure

Define.s value
Define.l timeout = 500

If OpenWindow(0, #PB_Ignore, #PB_Ignore, 200, 160, "Sender, wait for client") And CreateGadgetList(WindowID(0))
  SetWindowCallback(@MyWindowCallback())

  TextGadget(#lblText, 10, 12, 75, 20, "Text to send:")
  StringGadget(#txtText, 90, 10, 100, 20, "")
  ButtonGadget(#btnText, 50, 40, 100, 20, "Send Text")
  DisableGadget(#btnText, #True)
  TextGadget(#lblValue, 10, 102, 75, 20, "Value to send:")
  StringGadget(#txtValue, 90, 100, 100, 20, "", #PB_String_Numeric)
  ButtonGadget(#btnValue, 50, 130, 100, 20, "Send Value")
  DisableGadget(#btnValue, #True)

  While WindowEvent() : Wend

  Repeat

    Select WaitWindowEvent(timeout)
      Case #PB_Event_CloseWindow
        PostMessage_(#HWND_BROADCAST, MyMessage, #CloseMessage, 0)
        Break

      Case #PB_Event_Gadget

        Select EventGadget()
          Case #btnText
            value = GetGadgetText(#txtText)
            If value <> ""
              SendData(value)
            EndIf
          Case #btnValue
            value = GetGadgetText(#txtValue)
            If value <> ""
              SendMessage_(#HWND_BROADCAST, MyMessage, #SomeValue, Val(value))
            EndIf
        EndSelect

      Default
        If timeout
          If Not hWndClient ; wir warten noch auf den Clienten
            SendMessage_(#HWND_BROADCAST, MyMessage, #GetHwnd, 0)
          Else
            timout = 0
          EndIf
        EndIf
    EndSelect

  ForEver
EndIf
und hier der Client:

Code: Alles auswählen

; German forum: http://www.purebasic.fr/german/viewtopic.php?p=152509#152509
; Author: ts-soft
; Date: 20. May 2007
; OS: Windows
; Demo: No

Enumeration ; PrivatMessages
  #GetHwnd
  #SendHwnd
  #SomeValue
  #CloseMessage
  ; ...
EndEnumeration

Global MyMessage.l = RegisterWindowMessage_("MyOwnMessage")

Procedure MyWindowCallback(WindowID, Message, wParam, lParam)
  Protected Result.l = #PB_ProcessPureBasicEvents
  Protected *cd.COPYDATASTRUCT
  Select Message
    Case MyMessage
      Select wParam
        Case #GetHwnd ; der Sender erwartet mein Fensterhandle, also raus damit
          SendMessage_(#HWND_BROADCAST, MyMessage, #SendHwnd, WindowID(0))
        Case #SomeValue ; wir haben einen Zahlenwert erhalten
          If IsGadget(0)
            SetGadgetText(0, Str(lParam))
          EndIf
        Case #CloseMessage
          End 
      EndSelect
    Case #WM_COPYDATA ; der Sender hat einen String übermittelt
      *cd = lParam
      If IsGadget(0)
        SetGadgetText(0, PeekS(*cd\lpData))
      EndIf
  EndSelect

  ProcedureReturn Result
EndProcedure

If OpenWindow(0, #PB_Ignore, #PB_Ignore, 200, 40, "Client") And CreateGadgetList(WindowID(0))
  SetWindowCallback(@MyWindowCallback())
  TextGadget(0, 10, 10, 180, 20, "", #PB_Text_Center | #PB_Text_Border)
 
  While WaitWindowEvent() <> #PB_Event_CloseWindow : Wend

EndIf 
Copydata wird nur verwendet um auch Texte problemlos auszutauschen.

Ich hoffe mal, es kann jemand gebrauchen :)

// Edit
Unnötige Enumeration im clienten entfernt
und SendData ist jetzt Unicode-tauglich (beide müssen gleichen
Compilerschalter verwenden!)
// Edit
Hab jetzt noch das beenden des Clienten hinzugefügt, wenn der Sender
geschlossen wird

Verfasst: 20.05.2007 17:19
von mk-soft
:allright:

Schönes Beispiel. Geht einfacher als mit Pipes

FF :wink:

Verfasst: 20.05.2007 18:07
von legion
Super Sache !
Kommt wie gerufen !
Kann ich für meine DLL gut gebrauchen.
Danke !

Gruß Legion

Verfasst: 20.05.2007 18:34
von ts-soft
Schön wenns gefällt, ich finde es auch oft sinnvoller als mit pipes, mailslots usw.
legion hat geschrieben:Kann ich für meine DLL gut gebrauchen.
Danke !

Gruß Legion
Für DLLs ist es weniger sinnvoll. weil erstens haben die einen gemeinsamen
Memory mit der Anwendung und eine DLL hat meist auch kein Fensterhandle.

Da reichen dann Messages über #WM_USER + bla

Verfasst: 20.05.2007 21:14
von Leonhard
Mir ist da grad nen Bug im Code aufgefallen:

Wenn man 'Hallo, Welt!' sendet, bekommt man 'Hallo, '- und ein paar kästchen zurück. Das kommt immer im Unicode-Modus.

Mfg

leonhard

Verfasst: 20.05.2007 21:26
von ts-soft
Leonhard hat geschrieben:Mir ist da grad nen Bug im Code aufgefallen:

Wenn man 'Hallo, Welt!' sendet, bekommt man 'Hallo, '- und ein paar kästchen zurück. Das kommt immer im Unicode-Modus.

Mfg

leonhard
Hab das nur auf die schnelle reinimprovisiert :mrgreen:
Danke für den Hinweis, fehlende Klammern ergänzt!

Verfasst: 20.05.2007 22:34
von legion
ts-soft hat geschrieben: Für DLLs ist es weniger sinnvoll. weil erstens haben die einen gemeinsamen
Memory mit der Anwendung und eine DLL hat meist auch kein Fensterhandle.
Da reichen dann Messages über #WM_USER + bla
Danke für die Info !

Lg. Legion

Verfasst: 21.05.2007 09:21
von Tafkadasom2k5
ts-soft hat geschrieben:Für DLLs ist es weniger sinnvoll. weil erstens haben die einen gemeinsamen
Memory mit der Anwendung und eine DLL hat meist auch kein Fensterhandle.
Trotzdem kann es doch sein, dass sich dasnn Messages überschneiden..?

Von daher wäre es bei DLLs schon wichtig, die Nachricht von Windows registrieren zu lassen.

Wenn man eine Include benutzt dann wäre es was Anderes, weil man dann ja selber Kontrolle über verfügbare Konstanten hat, aber bei DLLs?
Ich würde bei DLLs wirklich über registerMessage_() auf die sichere Seite übergehen. Und unter Umständne sogar der komfortableren- wenn man nämlich eine Nachricht von der DLL empfangen soll, und das schlägt fehl wegen so eines Konstanten-Fehlers, dann wäre das eine Menge Aufwand, bis man den Fehler findet...

(oder sehe ich das völlig falsch, bzw von einem falschen Gesichtspunkt?
Wenn, dann bitte ich hiermit um AUfklärung ;)
Im Normalfall bin ich auch nicht lernresistent :mrgreen:)

Gr33tz
Tafkadasom2k5

Verfasst: 21.05.2007 15:18
von ts-soft
Eine DLL kann doch simpel #WM_USER + irgendwas nehmen, in wParam das
handle des Controls in lPararm der Wert. So machen es die meisten Controls,
die aus DLLs kommen, ist das einfachste. Dafür Messages Registrieren und
durchs ganze System zu jagen ist überflüssig :mrgreen: