WM_COPYDATA - Datentausausch zwischen Skripten

Hilfreiche Erklärungen und Tipps zum Lernen von Autohotkey

Moderator: jNizM

just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

WM_COPYDATA - Datentausausch zwischen Skripten

16 Jul 2020, 04:58

!!! In Bearbeitung !!!

Moin,

in diesem Beitrag habe ich angekündigt, etwas mehr über das Thema sagen zu wollen. Damit fange ich heute (16.07.20) mal an.

Für das Übermitteln von Daten von Skript zu Skript hat Microsoft die Nachricht und die zugehörige Datenstruktur entwickelt.

Um die zu nutzen braucht es Folgendes:
  • Alle beteiliegten Skripte müssen für das Senden das Handle (HWND) eines der Skriptfenster des Zielskripts kennen. Das darf auch das verborgene Hauptfenster des Skripts sein. Für Antworten kann das Zielfenster der eingehenden Nachricht entnommen werden.
  • In den empfangenden Skripten braucht man eine Funktion für die Behandlung eingehender Nachrichten

    Code: Select all

    OnMessage(0x004A, "MeineNachrichtenFunktion")
    mit den Parametern wParam und lParam.
  • Außerdem habe ich mal gelesen (finde es aber nicht mehr wieder), dass unter Umständen in emfangenden Skripten der Aufruf von

    Code: Select all

    DllCall("ChangeWindowMessageFilter", "UInt", 0x004A, "UInt", 1) ; WM_COPYDATA
    nötig sein soll, damit die Nachricht akzeptiert wird. Ich habe das aber bisher nicht gebraucht.
Und das war's auch schon.

Wie Ragnar unten angemerkt hat, gibt es für die Nutzung von WM_COPYDATA auch ein Beispiel in der Doku.
Last edited by just me on 17 Jul 2020, 05:48, edited 2 times in total.
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: WM_COPYDATA - Datentausausch zwischen Skripten

16 Jul 2020, 05:57

Daten:

Für den Datenttransport hat Microsoft die Struktur COPYDATASTRUCT bereitgestellt:

Code: Select all

typedef struct tagCOPYDATASTRUCT {
  ULONG_PTR dwData;
  DWORD     cbData;
  PVOID     lpData;
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
Diese Struktur besteht aus drei Feldern mit folgenden Inhalten:
  • ULONG_PTR dwData;
    Ein Feld in Pointergröße, das einen UINT (32-bit) Wert enthält. Hier kann Alles übergeben werden, was in 4 Bytes passt.
  • DWORD cbData;
    Die Anzahl der Bytes, auf die der im folgenden Feld lpData; enthaltene Pointer zeigt. Wenn kein Pointer übergeben wird, muss das Feld auf Null gesetzt werden.
  • PVOID lpData;
    Ein Pointer auf beliebige zusätzliche Daten oder Null.
Die ersten beiden Felder müssen in einer Länge von 4 Bytes gefüllt bzw. ausgelesen werden. Das letzte Feld enthält einen Pointer von 4 (32 Bit) bzw. 8 (64 Bit) bytes Länge. Wegen der Feldausrichtung in Strukturen ist die Distanz zwischen den Feldern immer A_PtrSize Bytes.

Code: Select all

VarSetCapacity(CDS, A_PtrSize * 3, 0) ; COPYDATASTRUCT
; Put:
NumPut(dwData, CDS, 0, "UInt")
NumPut(cbData, CDS, A_PtrSize, "UInt")
NumPut(lpData, CDS, A_PtrSize * 2, "UPtr")
; Get:
dwData := NumGet(CDS, 0, "UInt")
cbData := NumGet(CDS, A_PtrSize, "UInt")
lpData := NumGet(CDS, A_PtrSize * 2, "UPtr")
Per lpData können beliebige Inhalte übermittelt werden, Binärdaten und auch Text. Bei Text ist zu beachten, dass cbData die Länge in Bytes enthalten muss, nicht die Länge in Zeichen. Wenn alle Skripte dieselbe Sprache (ANSI / Unicode) sprechen, können Texte ohne Konvertierung übermittelt werden, anderenfalls sollten sie nach UTF-8 konvertiert werden. Für die Bestimmung, welcher Datentyp sich hinter lpData verbirgt, kann das Feld dwData benutzt werden (z.B. 1 = Text, 2 = Binärdaten).
Last edited by just me on 16 Jul 2020, 05:59, edited 1 time in total.
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: WM_COPYDATA - Datentausausch zwischen Skripten

16 Jul 2020, 05:58

Senden:
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: WM_COPYDATA - Datentausausch zwischen Skripten

16 Jul 2020, 06:00

Empfangen:
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: WM_COPYDATA - Datentausausch zwischen Skripten

16 Jul 2020, 06:01

Beispiele:

:arrow: Calling a function in another AHK multiple times concurrently
Master.ahk:

Code: Select all

#NoEnv
#SingleInstance, Force
OnExit, AppExit
DetectHiddenWindows, On
Global RunningScripts := {}
Global SizeT := A_IsUnicode ? 2 : 1
Global WM_COPYDATA := 0x004A
Params := "NotificationFlag`nRPA_Mode`nVariable4`nShortName`nLongname"
Script2Run := A_ScriptDir . "\Client.ahk"
; To call ChangeWindowMessageFilter is recommended by Microsoft for Win Vista+. Actually, I don't need it on Win 10.
DllCall("ChangeWindowMessageFilter", "UInt", WM_COPYDATA, "UInt", 1)
OnMessage(WM_COPYDATA, "WM_COPYDATA_Receive")
Gui, Margin, 100, 50
Gui, Add, Button, gRunScript, Run Next Client
Gui, Show, , % "Master (" . (A_ScriptHwnd + 0) . ")"
Return
GuiClose:
ExitApp
; ----------------------------------------------------------------------------------------------------------------------
RunScript:
Run, *Open %Script2Run%, , , PID
WinWait, ahk_pid %PID% ahk_class AutoHotkey, , 2
If (ErrorLevel) {
   MsgBox, 16, ERROR, Couldn't run %Script2Run%!
   Return
}
HMSG := WinExist()
If !WM_COPYDATA_Send(HMSG, Params) {
   MsgBox, 16, ERROR, Client %HMSG% refused didn't process WM_COPYDATA!
   WinClose, ahk_id %HMSG%
   Return
}
RunningScripts[HMSG] := PID
Return

AppExit:
   For HWND In RunningScripts
      WinClose, ahk_id %HWND%
ExitApp

Esc::
ExitApp

; ----------------------------------------------------------------------------------------------------------------------
WM_COPYDATA_Receive(W, L) {
   If !RunningScripts.HasKey(W)
      Return False
   RunningScripts.Delete(W)
   DataType := StrGet(L, "CP0")
   DataSize := NumGet(L + A_PtrSize, "Int")
   DataPtr := NumGet(L + (A_PtrSize * 2), "UPtr")
   If (DataType = "T") { ; we recieved text
      Data := StrGet(DataPtr, DataSize, "UTF-8")
      DataSize := StrLen(Data)
      MsgBox, Message from Client %W%`n`n%DataSize%`n`n%Data%
   }
   Else { ; we received binary data
      VarSetCapacity(Date, DataSize, 0)
      DllCall("RtlMoveMemory", "Ptr", &Data, "Ptr", DataPtr, "Ptr", DataSize)
   }
   Return True
}
; ----------------------------------------------------------------------------------------------------------------------
WM_COPYDATA_Send(HMSG, ByRef Data, DataSize := 0) {
   If DataSize Is Not Integer
      Return !(ErrorLevel := 1)
   If (DataSize < 1) {
      DataType := Asc("T")
      VarSetCapacity(UTF8, (DataSize := StrPut(Data, "UTF-8")), 0)
      StrPut(Data, &UTF8, "UTF-8")
      DataPtr := &UTF8
   }
   Else {
      DataType := Asc("B")
      DataPtr := &Data
   }
   VarSetCapacity(COPYDATA, 3 * A_PtrSize, 0)
   NumPut(DataType, COPYDATA, 0, "UChar")
   NumPut(DataSize, COPYDATA, A_PtrSize, "Int")
   NumPut(DataPtr, COPYDATA, 2 * A_PtrSize, "Ptr")
   SendMessage, WM_COPYDATA, A_ScriptHwnd, &COPYDATA, , ahk_id %HMSG%, , , , 1000
   Return (ErrorLevel = "FAIL") ? 0 : !!ErrorLevel
}
; ----------------------------------------------------------------------------------------------------------------------
/* http://msdn.microsoft.com/en-us/library/ms649010(VS.85).aspx
   typedef struct tagCOPYDATASTRUCT {
      ULONG_PTR dwData;     The data to be passed to the receiving application.
      DWORD     cbData;     The size, in bytes, of the data pointed to by the lpData member.
      PVOID     lpData;     The data to be passed to the receiving application. This member can be NULL.
   } COPYDATASTRUCT, *PCOPYDATASTRUCT;
*/
Client.ahk:

Code: Select all

#NoEnv
#SingleInstance Off
DetectHiddenWindows, On
Global HMSG := 0
Global SizeT := (A_IsUnicode ? 2 : 1)
Global Start := False
Global WM_COPYDATA := 0x004A
Global Data
Global DataSize
; To call ChangeWindowMessageFilter is recommended by Microsoft for Win Vista+. Actually, I don't need it on Win 10.
DllCall("ChangeWindowMessageFilter", "UInt", WM_COPYDATA, "UInt", 1)
OnMessage(WM_COPYDATA, "WM_COPYDATA_Receive")
While (Start = False)
   Sleep, 100
MsgBox, 0, % "Client (" . (A_ScriptHwnd + 0) . ")", Message from master %HMSG%`n`n%DataSize%`n`n%Data% ; we don't expect binary data
WM_COPYDATA_Send("This is the result sent from client " . (A_ScriptHwnd + 0))
ExitApp

Esc::
ExitApp

; ----------------------------------------------------------------------------------------------------------------------
WM_COPYDATA_Receive(W, L) {
   If (W <> HMSG) {
      If (HMSG = 0)
         HMSG := W
      Else
         Return False
   }
   DataType := StrGet(L, "CP0")
   DataSize := NumGet(L + A_PtrSize, "Int")
   DataPtr := NumGet(L + (A_PtrSize * 2), "UPtr")
   If (DataType = "T") { ; we recieved text
      Data := StrGet(DataPtr, DataSize, "UTF-8")
      DataSize := StrLen(Data)
   }
   Else { ; we received binary data
      VarSetCapacity(Data, DataSize, 0)
      DllCall("RtlMoveMemory", "Ptr", &Data, "Ptr", DataPtr, "Ptr", DataSize)
   }
   Start := True
   Return True
}
; ----------------------------------------------------------------------------------------------------------------------
WM_COPYDATA_Send(ByRef Data, DataSize := 0) {
   If DataSize Is Not Integer
      Return !(ErrorLevel := 1)
   If (DataSize < 1) {
      DataType := Asc("T")
      VarSetCapacity(UTF8, (DataSize := StrPut(Data, "UTF-8")), 0)
      StrPut(Data, &UTF8, "UTF-8")
      DataPtr := &UTF8
   }
   Else {
      DataType := Asc("B")
      DataPtr := &Data
   }
   VarSetCapacity(COPYDATA, 3 * A_PtrSize, 0)
   NumPut(DataType, COPYDATA, 0, "UChar")
   NumPut(DataSize, COPYDATA, A_PtrSize, "Int")
   NumPut(DataPtr, COPYDATA, 2 * A_PtrSize, "Ptr")
   SendMessage, WM_COPYDATA, A_ScriptHwnd, &COPYDATA, , ahk_id %HMSG%, , , , 1000
   Return (ErrorLevel = "FAIL") ? 0 : !!ErrorLevel
}
; ----------------------------------------------------------------------------------------------------------------------
/* http://msdn.microsoft.com/en-us/library/ms649010(VS.85).aspx
   typedef struct tagCOPYDATASTRUCT {
      ULONG_PTR dwData;     The data to be passed to the receiving application.
      DWORD     cbData;     The size, in bytes, of the data pointed to by the lpData member.
      PVOID     lpData;     The data to be passed to the receiving application. This member can be NULL.
   } COPYDATASTRUCT, *PCOPYDATASTRUCT;
*/
Last edited by just me on 19 Dec 2020, 04:01, edited 1 time in total.
User avatar
Ragnar
Posts: 613
Joined: 30 Sep 2013, 15:25

Re: WM_COPYDATA - Datentausausch zwischen Skripten

16 Jul 2020, 08:45

Nur als Randinformation: Für dieses Konzept gibt es bereits Beispiele in der Doku (OnMessage-Beispiel #3 für das Senden/Empfangen von Zahlen und OnMessage-Beispiel #4 für das Senden/Empfangen von Zeichenketten).
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: WM_COPYDATA - Datentausausch zwischen Skripten

16 Jul 2020, 15:56

@Ragnar,
stimmt! Die Beispiele beruhen aber sehr ausgeprägt auf AHK 1.0 Konzepten.

Return to “Tutorials”

Who is online

Users browsing this forum: No registered users and 11 guests