{ FTDXRC - CAT & Remote Control Software for YAESU FT DX 5000, FT-2000 and FT-950 https://df3cb.com/ftdxrc/ Copyright by Bernd Koch, DF3CB Inter-process communication with FTDXRC Encapsulation of the SendMessage API The Server registers a Windows Message using a SessionName. The SessionName is 'FT2000RC' for the communication with FTDXRC. The SessionName is the common name that the Server and Client will use to make a connection. Set the same name in the Server and Client. The Client connects to the server and sends a connect request. The Server accepts the connection and sends a Radio Data Record: TRadioDataRecord = packed record FrequencyA: Integer; FrequencyB: Integer; ModeA: Byte; ModeB: Byte; PowerLevel: Byte; PowerSwitch: Boolean; Split: Boolean; TX: Boolean; end; FrequencyA: is the current frequency of VFO A in Hz. FrequencyB: is the current frequency of VFO B in Hz. ModeA: is the current mode of VFO A (see YAESU modes below). ModeB: is the current mode of VFO B (see YAESU modes below). PowerLevel: is the transceiver's power output in W. PowerSwitch: is either off (false) or on (true). Split: is either off (false) or on (true). It indicates whether VFO A or VFO B is used on transmit. TX: is either off or on. It indicates whether you are receiving (false) or transmitting (true). YAESU modes: 0: ymLSB = SSB 1: ymUSB = SSB 2: ymCW = CW 3: ymFM = FM 4: ymAM = AM 5: ymFSK = RTTY 6: ymCW_R = CW 7: ymPKT_L = PKT 8: ymFSK_R = RTTY 9: ymPKT_FM = PKT 10: ymFM_N = FM 11: ymPKT_U = PKT The TRadioDataRecord is sent to the Client each time when any of the information has changed. For example when you tune VFO A or when you change bands or when you transmit. Server Properties Active: True if a valid handle was created. SessionName: This is the common name that the Server and Client will use to make a connection. Set the same name in the Server and Client. Server Methods: Create: Just like the others. Open: Open the server and make ready for client connection. Close: Broadcast message to all clients that server is closing and close hanlde. SendMsg: Send out this message to a client. Server Events: OnClientData: When a data packed is received. OnConnect: When a client is connected. OnDisconnected When a client is disconnected. OnAfteOpen Fired after the session is opened. OnAfterClose Fired after the session is closed. } unit IPC; interface uses Windows, Messages, SysUtils, Classes; const IPCConnectRequest = 'IPCConnectRequest'; IPCConnectResponse = 'IPCConnectResponse'; IPCClientDisconnecting = 'IPCClientDisconnecting'; IPCServerDisconnecting = 'IPCServerDisconnecting'; type TYaesuMode = (ymLSB, ymUSB, ymCW, ymFM, ymAM, ymFSK, ymCW_R, ymPKT_L, ymFSK_R, ymPKT_FM, ymFM_N, ymPKT_U); TRadioDataRecord = packed record FrequencyA: Integer; FrequencyB: Integer; ModeA: Byte; ModeB: Byte; PowerLevel: Byte; PowerSwitch: Boolean; Split: Boolean; TX: Boolean; end; PRadioDataRecord = ^TRadioDataRecord; TOnData = procedure(MsgPointer: Pointer) of object; TOnClientData = procedure(MsgPointer: Pointer; AHwnd: HWND) of object; TOnConnect = procedure(AHwnd: HWND) of object; TOnDisconnect = procedure(AHwnd: HWND) of object; TOnRadioData = procedure(RadioDataRecord: PRadioDataRecord) of object; TIPCServer = class(TComponent) private FActive: Boolean; FClientHwnd: HWND; FConnectRequestHwnd: DWORD; // 4-byte unsigned integer FConnectResponseHwnd: DWORD; // 4-byte unsigned integer FEnabled: Boolean; FFrequencyA: Integer; FFrequencyB: Integer; FModeA: TYaesuMode; FModeB: TYaesuMode; FPowerLevel: Byte; FPowerSwitch: Boolean; FServerDisconnectHwnd: DWORD; FSessionHandle: DWORD; // 4-byte unsigned integer FSessionName: string; FSplit: Boolean; FTX: Boolean; FWinHwnd: HWND; FOnAfterClose: TNotifyEvent; FOnAfterOpen: TNotifyEvent; FOnClientData: TOnClientData; FOnConnect: TOnConnect; FOnDisconnect: TOnDisconnect; procedure SendData; procedure SetEnabled(const Value: Boolean); procedure SetFrequencyA(const Value: Integer); procedure SetFrequencyB(const Value: Integer); procedure SetPowerLevel(const Value: Byte); procedure SetPowerSwitch(const Value: Boolean); procedure SetModeA(const Value: TYaesuMode); procedure SetModeB(const Value: TYaesuMode); procedure SetSplit(const Value: Boolean); procedure SetTX(const Value: Boolean); protected procedure WndProc(var AMsg: TMessage); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure SendMsg(MsgPointer: Pointer; AWinHwnd: HWND; ASize: DWORD); procedure Open; procedure Close; published property Active: Boolean read FActive default False; property Enabled: Boolean read FEnabled write SetEnabled default False; property FrequencyA: Integer read FFrequencyA write SetFrequencyA default 7000000; property FrequencyB: Integer read FFrequencyB write SetFrequencyB default 7000000; property ModeA: TYaesuMode read FModeA write SetModeA default ymCW; property ModeB: TYaesuMode read FModeB write SetModeB default ymCW; property PowerLevel: Byte read FPowerLevel write SetPowerLevel default 5; property PowerSwitch: Boolean read FPowerSwitch write SetPowerSwitch default False; property SessionName: string read FSessionName write FSessionName; property Split: Boolean read FSplit write SetSplit default False; property TX: Boolean read FTX write SetTX default False; property OnAfterClose: TNotifyEvent read FOnAfterClose write FOnAfterClose; property OnAfterOpen: TNotifyEvent read FOnAfterOpen write FOnAfterOpen; property OnClientData: TOnClientData read FOnClientData write FOnClientData; property OnConnect: TOnConnect read FOnConnect write FOnConnect; property OnDisconnect: TOnDisconnect read FOnDisconnect write FOnDisconnect; end; TIPCClient = class(TComponent) private FActive: Boolean; FClientDisconnectHwnd: DWORD; FConnectRequestHwnd: DWORD; FConnectResponseHwnd: DWORD; FServerDisconnectHwnd: DWORD; FServerWinHwnd: HWND; FSessionHandle: DWORD; FSessionName: string; FWinHwnd: HWND; FOnConnect: TOnConnect; FOnData: TOnData; FOnDisconnect: TOnDisconnect; FOnRadioData: TOnRadioData; protected procedure WndProc(var AMsg: TMessage); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure SendMsg(MsgPointer: Pointer; ASize: DWORD); procedure Open; procedure Close; published property Active: Boolean read FActive default False; property SessionName: string read FSessionName write FSessionName; property OnConnect: TOnConnect read FOnConnect write FOnConnect; property OnData: TOnData read FOnData write FOnData; property OnDisconnect: TOnDisconnect read FOnDisconnect write FOnDisconnect; property OnRadioData: TOnRadioData read FOnRadioData write FOnRadioData; end; implementation { ---------------------------------------------------------------------------- } { TIPCServer } { ---------------------------------------------------------------------------- } constructor TIPCServer.Create(AOwner: TComponent); begin inherited Create(AOwner); FActive := False; FClientHwnd := 0; FEnabled := False; FFrequencyA := 7000000; FFrequencyB := 7000000; FModeA := ymCW; FModeB := ymCW; FPowerLevel := 5; FPowerSwitch := False; FSessionHandle := 0; FSessionName := 'FT2000RC'; FSplit := False; FTX := False; FWinHwnd := 0; FConnectRequestHwnd := RegisterWindowMessage(IPCConnectRequest); FConnectResponseHwnd := RegisterWindowMessage(IPCConnectResponse); FServerDisconnectHwnd := RegisterWindowMessage(IPCServerDisconnecting); end; destructor TIPCServer.Destroy; begin if FWinHwnd <> 0 then begin SendMessage(HWND_BROADCAST, FServerDisconnectHwnd, FWinHwnd, 0); DeallocateHWND(FWinHwnd); end; inherited Destroy; end; procedure TIPCServer.Open; begin if FEnabled and (not FActive) then begin FSessionHandle := RegisterWindowMessage(PChar(FSessionName)); FWinHwnd := AllocateHWND(WndProc); if FWinHwnd <> 0 then begin FActive := True; if Assigned(FOnAfterOpen) then FOnAfterOpen(Self); end else raise Exception.Create('Cannot Allocate Window Handle'); end; end; procedure TIPCServer.Close; begin if FActive then begin if FWinHwnd <> 0 then begin SendMessage(HWND_BROADCAST, FServerDisconnectHwnd, FWinHwnd, LPARAM(FSessionHandle)); DeallocateHWND(FWinHwnd); FWinHwnd := 0; FClientHwnd := 0; FActive := False; if Assigned(FOnAfterClose) then FOnAfterClose(Self); end; end; end; procedure TIPCServer.WndProc(var AMsg: TMessage); var MsgPointer: Pointer; ClientHwnd: HWND; begin // Connect request from server if (AMsg.Msg = FConnectRequestHwnd) and (AMsg.lParam = LPARAM(FSessionHandle)) then begin if FActive then begin ClientHwnd := AMsg.wParam; if ClientHwnd <> 0 then begin FClientHwnd := ClientHwnd; SendMessage(ClientHwnd, FConnectResponseHwnd, FWinHwnd, 0); SendData; if Assigned(FOnConnect) then FOnConnect(ClientHwnd); end; end; end // Data message from server else if (AMsg.Msg = WM_COPYDATA) and (WPARAM(AMsg.wParam) = FSessionHandle) then begin MsgPointer := (TCopyDataStruct(Pointer(AMsg.lParam)^)).lpData; ClientHwnd := (TCopyDataStruct(Pointer(AMsg.lParam)^)).dwData; FClientHwnd := ClientHwnd; if Assigned(FOnClientData) then FOnClientData(MsgPointer, ClientHwnd); end; end; procedure TIPCServer.SendData; var CopyDataStruct: TCopyDataStruct; MyRecord: PRadioDataRecord; begin if (FActive) and (FClientHwnd <> 0) then begin GetMem(MyRecord, SizeOf(TRadioDataRecord)); try MyRecord.FrequencyA := FFrequencyA; MyRecord.FrequencyB := FFrequencyB; MyRecord.ModeA := Ord(TYaesuMode(FModeA)); MyRecord.ModeB := Ord(TYaesuMode(FModeB)); MyRecord.PowerLevel := FPowerLevel; MyRecord.PowerSwitch := FPowerSwitch; MyRecord.Split := FSplit; MyRecord.TX := FTX; CopyDataStruct.dwData := FSessionHandle; CopyDataStruct.cbData := SizeOf(TRadioDataRecord); CopyDataStruct.lpData := MyRecord; SendMessage(FClientHwnd, WM_COPYDATA, FSessionHandle, LPARAM(@CopyDataStruct)); finally FreeMem(MyRecord); end; end; end; procedure TIPCServer.SendMsg(MsgPointer: Pointer; AWinHwnd: HWND; ASize: DWORD); var CopyDataStruct: TCopyDataStruct; begin CopyDataStruct.dwData := 0; CopyDataStruct.cbData := ASize; CopyDataStruct.lpData := MsgPointer; SendMessage(AWinHwnd, WM_COPYDATA, FSessionHandle, LPARAM(@CopyDataStruct)); end; procedure TIPCServer.SetEnabled(const Value: Boolean); begin if Value <> FEnabled then begin if not Value then Close; FEnabled := Value; end; end; procedure TIPCServer.SetFrequencyA(const Value: Integer); begin if Value <> FFrequencyA then begin FFrequencyA := Value; SendData; end; end; procedure TIPCServer.SetFrequencyB(const Value: Integer); begin if Value <> FFrequencyB then begin FFrequencyB := Value; SendData; end; end; procedure TIPCServer.SetModeA(const Value: TYaesuMode); begin if Value <> FModeA then begin FModeA := Value; SendData; end; end; procedure TIPCServer.SetModeB(const Value: TYaesuMode); begin if Value <> FModeB then begin FModeB := Value; SendData; end; end; procedure TIPCServer.SetPowerLevel(const Value: Byte); begin if Value <> FPowerLevel then begin FPowerLevel := Value; SendData; end; end; procedure TIPCServer.SetPowerSwitch(const Value: Boolean); begin if Value <> FPowerSwitch then begin FPowerSwitch := Value; SendData; end; end; procedure TIPCServer.SetSplit(const Value: Boolean); begin if Value <> FSplit then begin FSplit := Value; SendData; end; end; procedure TIPCServer.SetTX(const Value: Boolean); begin if Value <> FTX then begin FTX := Value; SendData; end; end; { ---------------------------------------------------------------------------- } { TIPCClient } { ---------------------------------------------------------------------------- } constructor TIPCClient.Create(AOwner: TComponent); begin inherited Create(AOwner); FActive := False; FWinHwnd := 0; FServerWinHwnd := 0; FSessionHandle := 0; FSessionName := 'FT2000RC'; FServerDisconnectHwnd := RegisterWindowMessage(IPCServerDisconnecting); FConnectRequestHwnd := RegisterWindowMessage(IPCConnectRequest); FConnectResponseHwnd := RegisterWindowMessage(IPCConnectResponse); FClientDisconnectHwnd := RegisterWindowMessage(IPCClientDisconnecting); end; destructor TIPCClient.Destroy; begin if FWinHwnd <> 0 then DeallocateHWND(FWinHwnd); inherited Destroy; end; procedure TIPCClient.Open; begin if not FActive then begin FSessionHandle := RegisterWindowMessage(PChar(FSessionName)); FWinHwnd := AllocateHWND(WndProc); if FWinHwnd <> 0 then SendMessage(HWND_BROADCAST, FConnectRequestHwnd, FWinHwnd, LPARAM(FSessionHandle)); end; end; procedure TIPCClient.Close; begin if FActive then begin if FWinHwnd <> 0 then begin SendMessage(HWND_BROADCAST, FClientDisconnectHwnd, FWinHwnd, LPARAM(FSessionHandle)); DeallocateHWND(FWinHwnd); FWinHwnd := 0; FActive := False; end; end; end; procedure TIPCClient.WndProc(var AMsg: TMessage); var MsgPointer: Pointer; MyRecord: PRadioDataRecord; begin // Connect response from server if AMsg.Msg = FConnectResponseHwnd then begin if not FActive then begin FServerWinHwnd := AMsg.wParam; if Assigned(FOnConnect) then FOnConnect(FServerWinHwnd); FActive := True; end; end // Radio data message from server else if ((AMsg.Msg = WM_COPYDATA) and (WPARAM(AMsg.wParam) = FSessionHandle)) then begin MsgPointer := (TCopyDataStruct(Pointer(AMsg.lParam)^)).lpData; if Assigned(FOnData) then FOnData(MsgPointer); GetMem(MyRecord, SizeOf(TRadioDataRecord)); try MyRecord.FrequencyA := PRadioDataRecord((TCopyDataStruct(Pointer(AMsg.lParam)^)).lpData)^.FrequencyA; MyRecord.FrequencyB := PRadioDataRecord((TCopyDataStruct(Pointer(AMsg.lParam)^)).lpData)^.FrequencyB; MyRecord.ModeA := PRadioDataRecord((TCopyDataStruct(Pointer(AMsg.lParam)^)).lpData)^.ModeA; MyRecord.ModeB := PRadioDataRecord((TCopyDataStruct(Pointer(AMsg.lParam)^)).lpData)^.ModeB; MyRecord.PowerLevel := PRadioDataRecord((TCopyDataStruct(Pointer(AMsg.lParam)^)).lpData)^.PowerLevel; MyRecord.PowerSwitch := PRadioDataRecord((TCopyDataStruct(Pointer(AMsg.lParam)^)).lpData)^.PowerSwitch; MyRecord.Split := PRadioDataRecord((TCopyDataStruct(Pointer(AMsg.lParam)^)).lpData)^.Split; MyRecord.TX := PRadioDataRecord((TCopyDataStruct(Pointer(AMsg.lParam)^)).lpData)^.TX; if Assigned(FOnRadioData) then FOnRadioData(MyRecord); finally FreeMem(MyRecord); end; end // Disconnect message from server else if AMsg.Msg = FServerDisconnectHwnd then begin if FActive then begin if AMsg.wParam = WPARAM(FServerWinHwnd) then begin if Assigned(FOnDisconnect) then FOnDisconnect(FServerWinHwnd); FServerWinHwnd := 0; FActive := False; end; end; end; end; procedure TIPCClient.SendMsg(MsgPointer: Pointer; ASize: DWORD); var CopyDataStruct: TCopyDataStruct; begin if FServerWinHwnd <> 0 then begin CopyDataStruct.dwData := FWinHwnd; CopyDataStruct.cbData := ASize; CopyDataStruct.lpData := MsgPointer; SendMessage(FServerWinHwnd, WM_COPYDATA, FSessionHandle, LPARAM(@CopyDataStruct)); end; end; end.