Ich habe einen kleinen RPC - Server und Client auf Basis von ENM implementiert. Der Source wird mit dem nächsten Release mitgeliefert, da das aber noch etwas dauern wird poste ich den Code jetzt schonmal damit ihr damit spielen könnt

Aufgrund eines kleinen Bugs im Ping-Modul von ENM funktioniert der Code derzeit NICHT unter Unicode, das wird im nächsten Release behoben. Einfach als Ascii kompilieren oder das Ping-Modul in den Settings ausschalten.
Die Verwendung ist simpelst und sollte selbsterklärend sein. Es wurde bewusst auf komplexere Funktionen verzichtet (Speicher Blöcke zurückgeben, ...).
Die Remote-Proceduren dürfen jeden beliebigen PB - Standard Typen zurückgeben (Ascii/Byte/Character/Word/Long/Quad/Float/Double/String) außer Integer damit es keine Verwirrungen zwischen x86/x64 Client/Server gibt. Die Proceduren bekommen eine beliebige Anzahl von Parametern (auch jedes Typs) Als Variant - Liste übergeben.
Server-Beispielcode:
Code: Alles auswählen
XIncludeFile "ENM_SimpleRPC.pbi"
Procedure.d AddAll(*p.ENM_RPC_Parameter)
Protected All.d
ForEach *p\p()
All + *p\p()\d
Next
ProcedureReturn All
EndProcedure
Procedure.s ReverseStringR(*p.ENM_RPC_Parameter)
ProcedureReturn ReverseString(*p\p()\s)
EndProcedure
InitNetwork()
; Server erstellen
Define RPC.ENM_RPC_Server = ENM_RPC_NewServer(1024)
; Proceduren hinzufügen ...
RPC\AddProcedure("AddAll", #PB_Double, @AddAll())
RPC\AddProcedure("ReverseString", #PB_String, @ReverseStringR())
; Fensterschleife oder sonstwas
OpenConsole()
Input()
CloseConsole()
; Server beenden
RPC\Free()
Code: Alles auswählen
XIncludeFile "ENM_SimpleRPC.pbi"
InitNetwork()
; Client Verbinden:
Define RPCClient.ENM_RPC_Client = ENM_RPC_NewClient("127.0.0.1", 1024)
; Proceduren verbinden:
Define RemoteAddAll.ENM_RPC_Procedure_d = RPCClient\ConnectProcedure("AddAll", #PB_Double)
Define RemoteReverseString.ENM_RPC_Procedure_s = RPCClient\ConnectProcedure("ReverseString", #PB_String)
; Procedure 1 aufrufen:
; Das Variant - Einstellen kann man dann in Macros usw. einpacken, dann
; fällt das aiuch nicht mehr auf.
Define p.ENM_RPC_Parameter
AddElement(p\p()) : p\p()\Type = #PB_Double : p\p()\d = 1
AddElement(p\p()) : p\p()\Type = #PB_Double : p\p()\d = 2
AddElement(p\p()) : p\p()\Type = #PB_Double : p\p()\d = 3.3
AddElement(p\p()) : p\p()\Type = #PB_Double : p\p()\d = 4
; Remote Procedure aufrufen
Debug RemoteAddAll\Call(p)
; Kleiner Test mit String-Parameter
Define p2.ENM_RPC_Parameter
AddElement(p2\p()) : p2\p()\Type = #PB_String : p2\p()\s = "Hallo Welt"
; Remote aufrufen
Debug RemoteReverseString\Call(p2)
; Client freigeben
RPCClient\Free()
; Proceduren freigeben
RemoteAddAll\Free()
RemoteReverseString\Free()
Code: Alles auswählen
; ------------------------------------------------------------------------------------
; Easy Network Manager - RPC Server
; PB 4.5+ OS: Windows/Linux/MacOS Architecture: x86/x64
; Author: Alexander Aigner, cxAlex @ purebasic.fr/german
; ------------------------------------------------------------------------------------
; Include-Pfad anpassen!!
IncludePath "..\src\"
XIncludeFile "ENM.pbi"
EnableExplicit
; ENM Initialisieren
ENM_Init()
; Globale Defaults
Global _ENM_RPC_CB.ENM_Callbacks
Global _ENM_RPC_BluePrint.ENM_Protocol_Blueprint
; RPC - Variant Parameter
Structure _ENM_RPC_Variant
Type.a
StructureUnion
a.a
b.b
c.c
w.w
l.l
q.q
f.f
d.d
EndStructureUnion
s.s
EndStructure
; RPC-Parameter Structure
Structure ENM_RPC_Parameter
List p._ENM_RPC_Variant()
EndStructure
; Flag - Konstanten
Enumeration 1
#_ENM_RPC_REQ_FIND
#_ENM_RPC_REQ_EXEC
EndEnumeration
Enumeration 1
#_ENM_RPC_ANS_FIND
#_ENM_RPC_ANS_EXEC
EndEnumeration
; Prototypen für jeden Rückgabewert
Prototype.a _ENM_RPC_Server_Procedure_a(*p.ENM_RPC_Parameter)
Prototype.b _ENM_RPC_Server_Procedure_b(*p.ENM_RPC_Parameter)
Prototype.c _ENM_RPC_Server_Procedure_c(*p.ENM_RPC_Parameter)
Prototype.w _ENM_RPC_Server_Procedure_w(*p.ENM_RPC_Parameter)
Prototype.l _ENM_RPC_Server_Procedure_l(*p.ENM_RPC_Parameter)
Prototype.q _ENM_RPC_Server_Procedure_q(*p.ENM_RPC_Parameter)
Prototype.f _ENM_RPC_Server_Procedure_f(*p.ENM_RPC_Parameter)
Prototype.d _ENM_RPC_Server_Procedure_d(*p.ENM_RPC_Parameter)
Prototype.s _ENM_RPC_Server_Procedure_s(*p.ENM_RPC_Parameter)
; Variant Prototype für die Rückgabewerte
Structure _ENM_RPC_Server_Procedure_Variant
Type.a
StructureUnion
a._ENM_RPC_Server_Procedure_a
b._ENM_RPC_Server_Procedure_b
c._ENM_RPC_Server_Procedure_c
w._ENM_RPC_Server_Procedure_w
l._ENM_RPC_Server_Procedure_l
q._ENM_RPC_Server_Procedure_q
f._ENM_RPC_Server_Procedure_f
d._ENM_RPC_Server_Procedure_d
s._ENM_RPC_Server_Procedure_s
EndStructureUnion
EndStructure
; Daten für den Client:
Structure _ENM_RPC_Client_Client_Data
*VTable
kill.a
ConID.i
PT.ENM_Protocol_Transceiver ; Transceiver
ANSSignal.i
ANSType.i
ANS._ENM_RPC_Variant
EndStructure
; Daten für den Server
Structure _ENM_RPC_Server_Data
*VTable
Server.i
Map Functions._ENM_RPC_Server_Procedure_Variant()
GMutex.i
EndStructure
; Client-Data
Structure _ENM_RPC_Server_Client_Data
PT.ENM_Protocol_Transceiver ; Transceiver
EndStructure
; Daten für eine Procedure
Structure _ENM_RPC_Procedure_Data
*VTable
ProcName$
*Client._ENM_RPC_Client_Client_Data
EndStructure
; Steuer-Interface für den Server
Interface ENM_RPC_Server
AddProcedure(ProcedureName$, ReturnType, *Proc)
Free()
EndInterface
; Client - Interface
Interface ENM_RPC_Client
ConnectProcedure(ProcedureName$, ReturnType)
Free()
EndInterface
; RPC - Procedurecall
Macro _ENM_RPC_ProcedureX(_RtType)
Interface ENM_RPC_Procedure_#_RtType
Call._RtType(*p.ENM_RPC_Parameter)
Free()
EndInterface
EndMacro
; Interfaces für jeden RückgabeTyp
_ENM_RPC_ProcedureX(a)
_ENM_RPC_ProcedureX(b)
_ENM_RPC_ProcedureX(c)
_ENM_RPC_ProcedureX(w)
_ENM_RPC_ProcedureX(l)
_ENM_RPC_ProcedureX(q)
_ENM_RPC_ProcedureX(f)
_ENM_RPC_ProcedureX(d)
_ENM_RPC_ProcedureX(s)
; Params wandeln
Procedure.s _ENM_RPC_Params2Base64(List Params._ENM_RPC_Variant())
Protected gMem = GMem_New(1024) ; wachsender Speicher
Protected *Mem, MemSize, *BaseMem, BaseMemSize
Protected Base64$
ForEach Params()
Select Params()\Type
Case #PB_String
; länge des Strings schreiben
Params()\l = StringByteLength(Params()\s)
GMem_WriteData(gMem, @Params(), SizeOf(_ENM_RPC_Variant))
; und string hinten dran
GMem_WriteData(gMem, @Params()\s, StringByteLength(Params()\s) + SizeOf(Character))
Default ; einfach nur Daten schreiben
GMem_WriteData(gMem, @Params(), SizeOf(_ENM_RPC_Variant))
EndSelect
Next
; Base 64-Codedierung, alles in einen String packen...
MemSize = GMem_GetMemSize(gMem)
*Mem = GMem_GetMem(gMem)
If MemSize*2>=64
BaseMemSize = MemSize*2
Else
BaseMemSize = 64
EndIf
*BaseMem = AllocateMemory(BaseMemSize)
BaseMemSize = Base64Encoder(*Mem, MemSize, *BaseMem, BaseMemSize)
GMem_Free(gMem)
; Basedaten lesen
Base64$ = PeekS(*BaseMem, BaseMemSize)
FreeMemory(*BaseMem)
ProcedureReturn Base64$
EndProcedure
Procedure _ENM_RPC_Base642Params(Base64$, List Params._ENM_RPC_Variant())
Protected BaseMemSize, MemSize, *Mem, *BMem
ClearList(Params()) ; Alle Daten mal löschen...
; Base 64 decodieren
BaseMemSize = StringByteLength(Base64$)
If BaseMemSize*2>=64
MemSize = BaseMemSize*2
Else
MemSize = 64
EndIf
*Mem = AllocateMemory(MemSize)
*BMem = *Mem
MemSize = Base64Decoder(@Base64$, StringByteLength(Base64$), *Mem, MemSize)
; Speicher durchgehen, Einträge auslesen
While MemSize
AddElement(Params())
CopyMemory(*Mem, @Params(), SizeOf(_ENM_RPC_Variant))
MemSize - SizeOf(_ENM_RPC_Variant)
*Mem + SizeOf(_ENM_RPC_Variant)
If Params()\Type = #PB_String ; Sonderfall String...
CopyMemory(*Mem, @Params()\s, Params()\l + SizeOf(Character))
*Mem + (Params()\l + SizeOf(Character))
MemSize - (Params()\l + SizeOf(Character))
Params()\l = #Null
EndIf
Wend
; speicher freigebn
FreeMemory(*BMem)
EndProcedure
; Procedure ausführen und Rückgabewert speichern
Procedure _ENM_RPC_Server_ProcExecutor(PT.ENM_Protocol_Transceiver, *Proc._ENM_RPC_Server_Procedure_Variant, *p.ENM_RPC_Parameter)
PT\T_SetByte("rt_type", *Proc\Type) ; Typ speichern
; procedure aufrufen und rückgabewert speichern
Select *Proc\Type
Case #PB_Ascii : PT\T_SetByte("rt_a", *Proc\a(*p))
Case #PB_Byte : PT\T_SetByte("rt_a", *Proc\b(*p))
Case #PB_Character : PT\T_SetLong("rt_l", *Proc\c(*p))
Case #PB_Word : PT\T_SetLong("rt_l", *Proc\w(*p))
Case #PB_Long : PT\T_SetLong("rt_l", *Proc\l(*p))
Case #PB_Quad : PT\T_SetQuad("rt_q", *Proc\q(*p))
Case #PB_Float : PT\T_SetFloat("rt_f", *Proc\f(*p))
Case #PB_Double : PT\T_SetDouble("rt_d", *Proc\d(*p))
Case #PB_String : PT\T_SetString("rt_s", *Proc\s(*p))
EndSelect
EndProcedure
; Protocol-Handler für den Client...
Procedure _ENM_RPC_Client_ProtocolHandler(Client, PT.ENM_Protocol_Transceiver)
Protected flag, proc$, para$
Protected *cClient._ENM_RPC_Client_Client_Data
With *ENMRPCS
; Protokoll auslesen
flag = PT\R_GetByte("flag") ; Flag
proc$ = PT\R_GetString("proc") ; um welche Procedure geht es
; Client laden
*cClient = ENM\GetConnectionData(Client)
Select flag
Case #_ENM_RPC_ANS_FIND
*cClient\ANSType = #_ENM_RPC_ANS_FIND
*cClient\ANS\a = PT\R_GetByte("rt_a")
SignalSemaphore(*cClient\ANSSignal)
Case #_ENM_RPC_ANS_EXEC
*cClient\ANSType = #_ENM_RPC_ANS_EXEC
*cClient\ANS\Type = PT\R_GetByte("rt_type")
Select *cClient\ANS\Type
Case #PB_Ascii : *cClient\ANS\a = PT\R_GetByte("rt_a")
Case #PB_Byte : *cClient\ANS\b = PT\R_GetByte("rt_a")
Case #PB_Character : *cClient\ANS\c = PT\R_GetLong("rt_l")
Case #PB_Word : *cClient\ANS\w = PT\R_GetLong("rt_l")
Case #PB_Long : *cClient\ANS\l = PT\R_GetLong("rt_l")
Case #PB_Quad : *cClient\ANS\q = PT\R_GetQuad("rt_q")
Case #PB_Float : *cClient\ANS\f = PT\R_GetFloat("rt_f")
Case #PB_Double : *cClient\ANS\d = PT\R_GetDouble("rt_d")
Case #PB_String : *cClient\ANS\s = PT\R_GetString("rt_s")
EndSelect
SignalSemaphore(*cClient\ANSSignal)
EndSelect
EndWith
EndProcedure
; Protocol-Handler für den Server...
Procedure _ENM_RPC_Server_ProtocolHandler(Client, PT.ENM_Protocol_Transceiver, *ENMRPCS._ENM_RPC_Server_Data)
Protected flag, proc$, para$
Protected *mem, memsize
Protected *Proc._ENM_RPC_Server_Procedure_Variant
Protected p.ENM_RPC_Parameter
With *ENMRPCS
; Protokoll auslesen
flag = PT\R_GetByte("flag") ; Flag
proc$ = PT\R_GetString("proc") ; um welche Procedure geht es
Select flag
Case #_ENM_RPC_REQ_FIND
PT\T_SetByte("flag", #_ENM_RPC_ANS_FIND) ; Rückgabe: gefundene Procedure
LockMutex(\GMutex)
PT\T_SetByte("rt_a", (FindMapElement(\Functions(), LCase(proc$)) Or #Null))
UnlockMutex(\GMutex)
*mem = PT\T_Export(@memsize)
; Rückgabe Senden
ENM\SendPaket(Client, *mem, memsize)
FreeMemory(*mem)
Case #_ENM_RPC_REQ_EXEC
PT\T_SetByte("flag", #_ENM_RPC_ANS_EXEC) ; Rückgabe: ausgeführte Procedure
para$ = PT\R_GetString("para")
If para$ ; Parameter umwandeln
_ENM_RPC_Base642Params(para$, p\p())
EndIf
LockMutex(\GMutex)
*Proc = FindMapElement(\Functions(), LCase(proc$)) ; Procedure laden
UnlockMutex(\GMutex)
If *Proc ; Ausführen
_ENM_RPC_Server_ProcExecutor(PT, *Proc, @p) ; Ausführen und Rückgabewerte direkt speichern
EndIf
*mem = PT\T_Export(@memsize)
; Rückgabe Senden
ENM\SendPaket(Client, *mem, memsize)
FreeMemory(*mem)
EndSelect
EndWith
EndProcedure
; Netzwerk - Events
Procedure _ENM_RPC_NetEvent_Connect(Server, Client)
Protected *CData._ENM_RPC_Server_Client_Data
Protected *cCData._ENM_RPC_Client_Client_Data
If Server ; Server - Procedure
; Client anlegen
*CData = AllocateMemory(SizeOf(_ENM_RPC_Server_Client_Data))
*CData\PT = ENM\OpenProtocolTransceiver(_ENM_RPC_BluePrint)
ENM\SetConnectionData(Client, *CData)
Else ; Client - Procedure
; Initialisierung in Init...
EndIf
EndProcedure
Procedure _ENM_RPC_NetEvent_Disconnect(Server, Client)
Protected *CData._ENM_RPC_Server_Client_Data
Protected *cCData._ENM_RPC_Client_Client_Data
If Server ; Server - Procedure
; Alles freigeben
*CData = ENM\GetConnectionData(Client)
*CData\PT\Free()
FreeMemory(*CData)
Else ; Client - Procedure
; Connection beendet
*cCData = ENM\GetConnectionData(Client)
If *cCData\kill ; Killflag, Deinitialisieren
*cCData\PT\Free()
FreeSemaphore(*cCData\ANSSignal)
FreeMemory(*cCData)
Else
*cCData\ANSType = #Null
SignalSemaphore(*cCData\ANSSignal)
EndIf
EndIf
EndProcedure
Procedure _ENM_RPC_NetEvent_RecData(Server, Client)
Protected *CData._ENM_RPC_Server_Client_Data
Protected *cCData._ENM_RPC_Client_Client_Data
If Server ; Server - Procedure
Protected Pakets.ENM_Pakets = ENM\ReceivePakets(Client)
If Pakets ; Pakete emfangen
*CData = ENM\GetConnectionData(Client)
While Pakets\GetNext() ; Pakete durchgehen
If *CData\PT\R_Import(Pakets\GetMem(), Pakets\GetMemSize()) ; Protokoll lesen
_ENM_RPC_Server_ProtocolHandler(Client, *CData\PT, ENM\GetConnectionData(Server)) ; Protokoll abarbeiten
*CData\PT\R_Reset() ; zurücksetzen
EndIf
Wend
EndIf
Else ; Client - Procedure
Pakets.ENM_Pakets = ENM\ReceivePakets(Client)
If Pakets ; Pakete emfangen
*cCData = ENM\GetConnectionData(Client)
While Pakets\GetNext() ; Pakete durchgehen
If *cCData\PT\R_Import(Pakets\GetMem(), Pakets\GetMemSize()) ; Protokoll lesen
_ENM_RPC_Client_ProtocolHandler(Client, *cCData\PT) ; Protokoll abarbeiten
*cCData\PT\R_Reset() ; zurücksetzen
EndIf
Wend
EndIf
EndIf
EndProcedure
Procedure _ENM_RPC_NetEvent_RecFile(Server, Client)
; Obsolete....
EndProcedure
; Initialisierung
Procedure _ENM_RPC_Init()
Shared _ENM_RPC_CB.ENM_Callbacks
Shared _ENM_RPC_BluePrint.ENM_Protocol_Blueprint
Protected ENMRPCProtoBP$ ; RPC - Protokoll
ENMRPCProtoBP$ = "PN: ENM_RPC" + #LFCR$
ENMRPCProtoBP$ + "PV: 1" + #LFCR$
ENMRPCProtoBP$ + "flag I: 0 T: BYTE D: 0" + #LFCR$
ENMRPCProtoBP$ + "proc I: 1 T: STRING D: " + #LFCR$
ENMRPCProtoBP$ + "para I: 2 T: STRING D: " + #LFCR$
ENMRPCProtoBP$ + "rt_type I: 3 T: BYTE D: 0" + #LFCR$
ENMRPCProtoBP$ + "rt_a I: 4 T: BYTE D: 0" + #LFCR$
ENMRPCProtoBP$ + "rt_l I: 5 T: LONG D: 0" + #LFCR$
ENMRPCProtoBP$ + "rt_q I: 6 T: QUAD D: 0" + #LFCR$
ENMRPCProtoBP$ + "rt_f I: 7 T: FLOAT D: 0.0000000000" + #LFCR$
ENMRPCProtoBP$ + "rt_d I: 8 T: DOUBLE D: 0.0000000000" + #LFCR$
ENMRPCProtoBP$ + "rt_s I: 9 T: STRING D: "
; Blueprint erstellen
_ENM_RPC_BluePrint = ENM\OpenProtocolBlueprint(ENMRPCProtoBP$)
With _ENM_RPC_CB
\Connect = @_ENM_RPC_NetEvent_Connect()
\Disconnect = @_ENM_RPC_NetEvent_Disconnect()
\Receive = @_ENM_RPC_NetEvent_RecData()
\ReceiveFile = @_ENM_RPC_NetEvent_RecFile()
EndWith
EndProcedure : _ENM_RPC_Init()
; Neue Verbindung:
Procedure ENM_RPC_NewClient(ServerHost$, ServerPort)
Protected *ENMRPCC._ENM_RPC_Client_Client_Data = AllocateMemory(SizeOf(_ENM_RPC_Client_Client_Data))
With *ENMRPCC
\ConID = ENM\OpenConnection(ServerHost$, ServerPort)
If \ConID ; Connection konnte erstellt werden
ENM\SetCallbacks(\ConID, _ENM_RPC_CB) ; Default - Callbacks
\VTable = ?_ENM_RPC_Client_VTable
\PT = ENM\OpenProtocolTransceiver(_ENM_RPC_BluePrint)
\ANSSignal = CreateSemaphore()
ENM\SetConnectionData(\ConID, *ENMRPCC)
Else
FreeMemory(*ENMRPCC)
*ENMRPCC = #Null
EndIf
EndWith
ProcedureReturn *ENMRPCC
EndProcedure
Procedure _ENM_RPC_Client_ConnectProcedure(*ENMRPCC._ENM_RPC_Client_Client_Data, ProcedureName$, ReturnType)
Protected *Mem, MemSize
Protected *ENMRPCP._ENM_RPC_Procedure_Data
With *ENMRPCC
; Protokol-Anfrage senden
\PT\T_SetByte("flag", #_ENM_RPC_REQ_FIND)
\PT\T_SetString("proc", ProcedureName$)
*Mem = \PT\T_Export(@MemSize)
\PT\T_Reset() ; Transmitter-Reset
; Senden
ENM\SendPaket(\ConID, *Mem, MemSize)
FreeMemory(*Mem)
; Auf Antwort warten
WaitSemaphore(\ANSSignal)
Select \ANSType
Case #_ENM_RPC_REQ_FIND
; Element erstellen
*ENMRPCP = AllocateMemory(SizeOf(_ENM_RPC_Procedure_Data))
InitializeStructure(*ENMRPCP, _ENM_RPC_Procedure_Data)
; Client und ProcedureName speichern
*ENMRPCP\Client = *ENMRPCC
*ENMRPCP\ProcName$ = ProcedureName$
Select ReturnType ; VTable suchen
Case #PB_Ascii : *ENMRPCP\VTable = ?_ENM_RPC_Procedure_VTable_a
Case #PB_Byte : *ENMRPCP\VTable = ?_ENM_RPC_Procedure_VTable_b
Case #PB_Character : *ENMRPCP\VTable = ?_ENM_RPC_Procedure_VTable_c
Case #PB_Word : *ENMRPCP\VTable = ?_ENM_RPC_Procedure_VTable_w
Case #PB_Long : *ENMRPCP\VTable = ?_ENM_RPC_Procedure_VTable_l
Case #PB_Quad : *ENMRPCP\VTable = ?_ENM_RPC_Procedure_VTable_q
Case #PB_Float : *ENMRPCP\VTable = ?_ENM_RPC_Procedure_VTable_f
Case #PB_Double : *ENMRPCP\VTable = ?_ENM_RPC_Procedure_VTable_d
Case #PB_String : *ENMRPCP\VTable = ?_ENM_RPC_Procedure_VTable_s
EndSelect
EndSelect
EndWith
ProcedureReturn *ENMRPCP
EndProcedure
; Client freigeben
Procedure _ENM_RPC_Client_Free(*ENMRPCC._ENM_RPC_Client_Client_Data)
Protected *Mem, MemSize
Protected *ENMRPCP._ENM_RPC_Procedure_Data
With *ENMRPCC
; Kill-Flag setzen
\kill = #True
; DeInit ist im Close-Event
ENM\CloseConnection(\ConID)
EndWith
EndProcedure
; Procedure freigeben
Procedure _ENM_RPC_Procedure_Free(*ENMRPCP._ENM_RPC_Procedure_Data)
With *ENMRPCP
ClearStructure(*ENMRPCP, _ENM_RPC_Procedure_Data)
FreeMemory(*ENMRPCP)
EndWith
EndProcedure
Macro _ENM_RPC_Procedure_CallX(_Name, _Type)
Procedure._Name _ENM_RPC_Procedure_Call_#_Name(*ENMRPCP._ENM_RPC_Procedure_Data, *p.ENM_RPC_Parameter)
Protected *Mem, MemSize
; Anfrage schreiben
*ENMRPCP\Client\PT\T_SetByte("flag", #_ENM_RPC_REQ_EXEC)
*ENMRPCP\Client\PT\T_SetString("proc", *ENMRPCP\ProcName$)
*ENMRPCP\Client\PT\T_SetString("para", _ENM_RPC_Params2Base64(*p\p()))
*Mem = *ENMRPCP\Client\PT\T_Export(@MemSize)
*ENMRPCP\Client\PT\T_Reset()
; Anfrage senden
ENM\SendPaket(*ENMRPCP\Client\ConID, *Mem, MemSize)
FreeMemory(*Mem)
; Auf Antwort warten
WaitSemaphore(*ENMRPCP\Client\ANSSignal)
Select *ENMRPCP\Client\ANSType
Case #_ENM_RPC_REQ_EXEC ; Richtiger Return Type?
If *ENMRPCP\Client\ANS\Type = _Type
ProcedureReturn *ENMRPCP\Client\ANS\_Name
EndIf
EndSelect
EndProcedure
EndMacro
; Caller-Proceduren
_ENM_RPC_Procedure_CallX(a, #PB_Ascii)
_ENM_RPC_Procedure_CallX(b, #PB_Byte)
_ENM_RPC_Procedure_CallX(c, #PB_Character)
_ENM_RPC_Procedure_CallX(w, #PB_Word)
_ENM_RPC_Procedure_CallX(l, #PB_Long)
_ENM_RPC_Procedure_CallX(q, #PB_Quad)
_ENM_RPC_Procedure_CallX(f, #PB_Float)
_ENM_RPC_Procedure_CallX(d, #PB_Double)
_ENM_RPC_Procedure_CallX(s, #PB_String)
; Neuen Server
Procedure ENM_RPC_NewServer(ServerPort)
Protected *ENMRPCS._ENM_RPC_Server_Data = AllocateMemory(SizeOf(_ENM_RPC_Server_Data))
InitializeStructure(*ENMRPCS, _ENM_RPC_Server_Data)
With *ENMRPCS
\VTable = ?_ENM_RPC_Server_VTable ; VTable
\Server = ENM\OpenServer(ServerPort) ; Server öffnen
If \Server
ENM\SetCallbacks(\Server, _ENM_RPC_CB) ; Default - Callbacks
\GMutex = CreateMutex() ; Schutzmutex
ENM\SetConnectionData(\Server, *ENMRPCS) ; Daten im Server-Handle speichern
Else ; Alles Freigeben, Port nicht frei
ClearStructure(*ENMRPCS, _ENM_RPC_Server_Data)
FreeMemory(*ENMRPCS)
*ENMRPCS = #Null
EndIf
EndWith
; Zurückgeben
ProcedureReturn *ENMRPCS
EndProcedure
; Procedure Hinzufügen
Procedure _ENM_RPC_Server_AddProcedure(*ENMRPCS._ENM_RPC_Server_Data, ProcedureName$, ReturnType, *Proc)
With *ENMRPCS
LockMutex(\GMutex)
AddMapElement(\Functions(), LCase(ProcedureName$))
\Functions()\Type = ReturnType
\Functions()\a = *Proc
UnlockMutex(\GMutex)
EndWith
EndProcedure
; Server Beenden
Procedure _ENM_RPC_Server_Free(*ENMRPCS._ENM_RPC_Server_Data, ProcedureName$, ReturnType, *Proc)
With *ENMRPCS
ENM\CloseServer(\Server) ; Server Beenden
; Structur freigeben
ClearStructure(*ENMRPCS, _ENM_RPC_Server_Data)
FreeMemory(*ENMRPCS)
EndWith
EndProcedure
; VTables für Procedure-Typen
Macro _ENM_RPC_Procedure_VTableX(_Type)
_ENM_RPC_Procedure_VTable_#_Type:
Data.i @_ENM_RPC_Procedure_Call_#_Type()
Data.i @_ENM_RPC_Procedure_Free()
EndMacro
; DataSection
DataSection
_ENM_RPC_Server_VTable:
Data.i @_ENM_RPC_Server_AddProcedure()
Data.i @_ENM_RPC_Server_Free()
_ENM_RPC_Client_VTable:
Data.i @_ENM_RPC_Client_ConnectProcedure()
Data.i @_ENM_RPC_Client_Free()
; VTables für Procedure-Typen
_ENM_RPC_Procedure_VTableX(a)
_ENM_RPC_Procedure_VTableX(b)
_ENM_RPC_Procedure_VTableX(c)
_ENM_RPC_Procedure_VTableX(w)
_ENM_RPC_Procedure_VTableX(l)
_ENM_RPC_Procedure_VTableX(q)
_ENM_RPC_Procedure_VTableX(f)
_ENM_RPC_Procedure_VTableX(d)
_ENM_RPC_Procedure_VTableX(s)
EndDataSection

Gruß, Alex