#include-once

Global Const $MHVersionInformation = "V1.04"

Global $MHSendDataToFunc = " _DefaultMsgFunc"
Global $MHhwmd_Receiver
Global $MHAdditionalIdentifier = "_CAL987qwerty2468";just to make the window titles a little bit more unique

; Windows Definitions;
Global Const $StructDef_COPYDATA = "dword none;dword count;ptr pointer"
Global Const $WM_COPYDATA_MH = 0x4A

;Message Queue Setup
Global $MHCallBackTimer = 200
Global $pTimerProc, $uiTimer
Global $aMessageQueue[1]=[0]

;~ Opt("OnExitFunc", "CallBack_Exit")

; #FUNCTION# ====================================================================================================================
; Name...........: _MHVersion
; Description ...: Gets Message Handler Version information
; Syntax.........: _MHVersion()
; Parameters ....: None
; Return values .: Message Handler Version Number
; Author ........: ChrisL
; ===============================================================================================================================
Func _MHVersion()

	Return $MHVersionInformation

EndFunc


; #FUNCTION# ====================================================================================================================
; Name...........: _SetCallBackTimerInterval
; Description ...: Sets the script up to accept messages
; Syntax.........: _SetCallBackTimerInterval(nnn)
; Parameters ....: $iTime     - Callback timer in milliseconds to process each message in the queue
; Return values .: Success    - The new callback timer
;                  Failure    - @error is set and the previous callback timer setting is used. Default is 200ms
; Information ...: The timer must be changed before _SetAsReceiver() is called
; Author ........: ChrisL
; ===============================================================================================================================
Func _SetCallBackTimerInterval($iTime = 200)
	If Not IsInt($iTime) then Return SetError(1,0,$MHCallBackTimer) ;not an integer so set error and return current setting
	If $MHhwmd_Receiver <> "" then Return SetError(1,0,$MHCallBackTimer) ;the receiver function is already set so the timer can not be changed
	If $iTime = 0 then Return $MHCallBackTimer ;Return existing value
	If $iTime < 50 then $iTime = 50
	$MHCallBackTimer = $iTime
	Return SetError(0,0,$iTime)
EndFunc


; #FUNCTION# ====================================================================================================================
; Name...........: _CALLBACKQUEUE
; Description ...: Process any messages that are queued and adjust the message queue
; Syntax.........: None
; Parameters ....: None
; Return values .: None
; Author ........: ChrisL
; ===============================================================================================================================
Func _CALLBACKQUEUE()

	Local $vMessage
	Local $queueLen = $aMessageQueue[0]

	If $queueLen > 0 then
		$vMessage = $aMessageQueue[1]
		$aMessageQueue[1] = ""
		For $i = 1 to $queueLen -1
			$aMessageQueue[$i] = $aMessageQueue[$i +1]
		Next

		Redim $aMessageQueue[$queueLen]

		$aMessageQueue[0] = $queueLen - 1
		Call($MHSendDataToFunc,$vMessage)
	EndIf

EndFunc


; #FUNCTION# ====================================================================================================================
; Name...........: _QueueMessage
; Description ...: Queues messages to prevent script slowdown
; Syntax.........: _QueueMessage($vText)
; Parameters ....: $vText    - The text to queue from the remote script
; Return values .: None
; Author ........: ChrisL
; ===============================================================================================================================
Func _QueueMessage($vText)
	Redim $aMessageQueue[$aMessageQueue[0] +2]
	$aMessageQueue[$aMessageQueue[0] +1] = $vText
	$aMessageQueue[0]+=1
EndFunc


; #FUNCTION# ====================================================================================================================
; Name...........: _SetAsReceiver
; Description ...: Sets the script up to accept messages
; Syntax.........: _SetAsReceiver($vTitle)
; Parameters ....: $vTitle    - The Local_ReceiverID_Name
; Return values .: Success    - Handle to the receiver window
;                  Failure    - @error is set and the relevant message is displayed
; Author ........: ChrisL
; ===============================================================================================================================
Func _SetAsReceiver($vTitle)
	If StringLen($vTitle) = 0 then
		Msgbox(16 + 262144,"Message Handler Error","A Local_ReceiverID_Name must be specified." & @crlf & _
			"Messages will not be received unless a unique Local_ReceiverID_Name is used!")
		Return SetError(1,1,-1);Make sure the user has specified a title
	EndIf

	$vTitle &= $MHAdditionalIdentifier;add on our additionalIdentifier which is unlikely to be used exept by scripts using this UDF

	If WInExists($vtitle) and WinGetHandle($vTitle) <> $MHhwmd_Receiver then ;already a window exists with this title and it's not ours highly unlikely unless 2 copies of the script are running
		Msgbox(16 + 262144,"Message Handler Error","The Local_ReceiverID_Name " & StringTrimRight($vTitle,StringLen($MHAdditionalIdentifier)) & " already exists." & @crlf & _
			"A unique Local_ReceiverID_Name must be specified." & @crlf & _
			"Messages will not be received unless a unique Local_ReceiverID_Name is used!")
		Return SetError(1,2,-1)
	EndIf

	$MHhwmd_Receiver = GUICreate($vTitle)
	GUIRegisterMsg($WM_COPYDATA_MH, "_GUIRegisterMsgProc")
	$pTimerProc = DllCallbackRegister("_CALLBACKQUEUE", "none", "")
	$uiTimer = DllCall("user32.dll", "uint", "SetTimer", "hwnd", 0, "uint", 0, "int", $MHCallBackTimer, "ptr", DllCallbackGetPtr($pTimerProc))
	$uiTimer = $uiTimer[0]
	Return $MHhwmd_Receiver

EndFunc


; #FUNCTION# ====================================================================================================================
; Name...........: _SetReceiverFunction
; Description ...: Sets the function to call on receiving data
; Syntax.........: _SetReceiverFunction($vString)
; Parameters ....: $vString   - The string of data to send
; Return values .: Success    - The users function name to call
;                  Failure    - @error is set and "" is returned
; Author ........: ChrisL
; ===============================================================================================================================
Func _SetReceiverFunction($vString)
	If $vString = "" then return SetError(1,1,$vString)
	$MHSendDataToFunc = $vString
	Return SetError(0,0,$MHSendDataToFunc)
EndFunc


; #FUNCTION# ====================================================================================================================
; Name...........: _SendData
; Description ...: Sends data to the registered window
; Syntax.........: _SendData($vData,$ReceiverTitle)
; Parameters ....: $vData           - The string of data to send
;                  $ReceiverTitle   - Remote_ReceiverID_Name specified in the script you wish to communicate to
; Return values .: Success      - The count of the string length sent
;                  Failure      - @error is set and 0 is returned
; Author ........: martin, piccaso and ChrisL
; ===============================================================================================================================
Func _SendData($vData,$ReceiverTitle)
	Local $strLen,$CDString,$vs_cds,$pCDString,$pStruct,$hwndRec

	If StringLen($ReceiverTitle) = 0 then Return SetError(1,1,0);Make sure the user has specified a title
	$ReceiverTitle&= $MHAdditionalIdentifier

	$strLen = StringLen($vData)
	$CDString = DllStructCreate("char var1[" & $strLen +1 & "]");the array to hold the string we are sending
	DllStructSetData($CDString,1,$vData)

	$pCDString = DllStructGetPtr($CDString);the pointer to the string

	$vs_cds = DllStructCreate($StructDef_COPYDATA);create the message struct
	DllStructSetData($vs_cds,"count",$strLen + 1);tell the receiver the length of the string +1
	DllStructSetData($vs_cds,"pointer",$pCDString);the pointer to the string

	$pStruct = DllStructGetPtr($vs_cds)

	$hwndRec = WinGetHandle($ReceiverTitle)
	If $hwndRec = "" then
		$vs_cds = 0;free the struct
		$CDString = 0;free the struct
		Return SetError(1,2,0)
	EndIf


	DllCall("user32.dll", "lparam", "SendMessage", "hwnd", $hwndRec, "int", $WM_COPYDATA_MH, "wparam", 0, "lparam", $pStruct)
	If @error then
		$vs_cds = 0;free the struct
		$CDString = 0;free the struct
		return SetError(1, 3, 0) ;return 0 no data sent
	EndIf


	$vs_cds = 0;free the struct
	$CDString = 0;free the struct
	Return $strLen
EndFunc



; #FUNCTION# ====================================================================================================================
; Name...........: _GUIRegisterMsgProc
; Description ...: Called when a messae is sent to the registered window
; Syntax.........: _GUIRegisterMsgProc($hWnd, $MsgID, $wParam, $lParam)
; Parameters ....: $hWnd       - Window/control handle
;                  $iMsg       - Message ID received
;                  $wParam     - Could specify additional message-specific information
;                  $lParam     - Specifies a pointer to the message
; Return values .: None        - Calls user specified function
; Author ........: piccaso
; Modified.......: ChrisL and martin
; ===============================================================================================================================
Func _GUIRegisterMsgProc($hWnd, $MsgID, $WParam, $LParam)
	Local $vs_cds,$vs_msg

    If $MsgID = $WM_COPYDATA_MH Then ; We Recived a WM_COPYDATA Message
       ; $LParam = Poiter to a COPYDATA Struct
        $vs_cds = DllStructCreate($StructDef_COPYDATA, $LParam)
       ; Member No. 3 of COPYDATA Struct (PVOID lpData;) = Pointer to Custom Struct
        $vs_msg = DllStructCreate("char[" & DllStructGetData($vs_cds, "count") & "]", DllStructGetData($vs_cds, "pointer"))
       ; Call the function to queue the received data
		_QueueMessage(DllStructGetData($vs_msg, 1))
		$vs_cds = 0
		$vs_msg = 0
    EndIf

EndFunc  ;==>_GUIRegisterMsgProc


; #FUNCTION# ====================================================================================================================
; Name...........: _DefaultMsgFunc
; Description ...: If no user function is specified this function i used to receive data
; Syntax.........: _DefaultMsgFunc($vText)
; Parameters ....: $vText     - The data sent be the other script
; Return values .: None
; Author ........: ChrisL
; ===============================================================================================================================
Func _DefaultMsgFunc($vText)
	Msgbox(0," _DefaultMsgFunc",$vText)
EndFunc  ;==>_DefaultMsgFunc


;Release the CallBack resources
Func CallBack_Exit()
	If $MHhwmd_Receiver <> "" then
		DllCallbackFree($pTimerProc)
		DllCall("user32.dll", "int", "KillTimer", "hwnd", 0, "uint", $uiTimer)
	EndIf
EndFunc
