{ This file is part of the Free Component library. Copyright (c) 2005 by Michael Van Canneyt, member of the Free Pascal development team Windows implementation of one-way IPC between 2 processes See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} uses Windows,messages; Const MsgWndClassName : pwidechar = 'FPCMsgWindowCls'; Resourcestring SErrFailedToRegisterWindowClass = 'Failed to register message window class'; SErrFailedToCreateWindow = 'Failed to create message window %s'; var MsgWindowClass: TWndClass = ( style: 0; lpfnWndProc: Nil; cbClsExtra: 0; cbWndExtra: 0; hInstance: 0; hIcon: 0; hCursor: 0; hbrBackground: 0; lpszMenuName: nil; lpszClassName: Nil); { --------------------------------------------------------------------- TWinMsgServerComm ---------------------------------------------------------------------} Type TWinMsgServerComm = Class(TIPCServerComm) Private FHWND : HWND; FWindowName : Widestring; FDataPushed : Boolean; Function AllocateHWnd(const cwsWindowName : widestring) : HWND; Public Constructor Create(AOwner : TSimpleIPCServer); override; procedure ReadMsgData(var Msg: TMsg); Procedure StartServer; override; Procedure StopServer; override; Function PeekMessage(TimeOut : Integer) : Boolean; override; Procedure ReadMessage ; override; Function GetInstanceID : String;override; Property WindowName : WideString Read FWindowName; end; function MsgWndProc(HWindow: HWnd; Message, WParam, LParam: Longint): Longint;stdcall; Var I : TWinMsgServerComm; Msg : TMsg; begin Result:=0; If (Message=WM_COPYDATA) then begin I:=TWinMsgServerComm(GetWindowLong(HWindow,GWL_USERDATA)); If (I<>NIl) then begin Msg.Message:=Message; Msg.WParam:=WParam; Msg.LParam:=LParam; I.ReadMsgData(Msg); I.FDataPushed:=True; If Assigned(I.Owner.OnMessage) then I.Owner.ReadMessage; Result:=1; end end else Result:=DefWindowProc(HWindow,Message,WParam,LParam); end; function TWinMsgServerComm.AllocateHWnd(const cwsWindowName: Widestring): HWND; var cls: LPWNDCLASS; isreg : Boolean; begin Pointer(MsgWindowClass.lpfnWndProc):=@MsgWndProc; MsgWindowClass.hInstance := HInstance; MsgWindowClass.lpszClassName:=MsgWndClassName; isreg:=GetClassInfo(HInstance,MsgWndClassName,cls); if not isreg then if (Windows.RegisterClass(MsgWindowClass)=0) then Owner.DoError(SErrFailedToRegisterWindowClass,[]); Result:=CreateWindowEx(WS_EX_TOOLWINDOW, MsgWndClassName, PWidechar(cwsWindowName), WS_POPUP {!0}, 0, 0, 0, 0, 0, 0, HInstance, nil); if (Result=0) then Owner.DoError(SErrFailedToCreateWindow,[cwsWindowName]); SetWindowLong(Result,GWL_USERDATA,Longint(Self)); end; constructor TWinMsgServerComm.Create(AOWner: TSimpleIPCServer); begin inherited Create(AOWner); FWindowName:=Owner.ServerID; If not Owner.Global then FWindowName:=FWindowName+'_'+InstanceID; end; procedure TWinMsgServerComm.StartServer; begin FHWND:=AllocateHWND(FWindowName); end; procedure TWinMsgServerComm.StopServer; begin DestroyWindow(FHWND); FHWND:=0; end; function TWinMsgServerComm.PeekMessage(TimeOut: Integer): Boolean; Var Msg : Tmsg; B : Boolean; R : DWORD; begin Result:=FDataPushed; If Result then Exit; B:=Windows.PeekMessage(Msg, FHWND, 0, 0, PM_NOREMOVE); If not B then // No message yet. Wait for a message to arrive available within specified time. begin if (TimeOut=0) then TimeOut:=Integer(INFINITE); R:=MsgWaitForMultipleObjects(1,FHWND,False,TimeOut,QS_SENDMESSAGE); B:=(R<>WAIT_TIMEOUT); end; If B then Repeat B:=Windows.PeekMessage(Msg, FHWND, 0, 0, PM_NOREMOVE); if B then begin Result:=(Msg.Message=WM_COPYDATA); // Remove non WM_COPY messages from Queue if not Result then GetMessage(@Msg,FHWND,0,0); end; Until Result or (not B); end; procedure TWinMsgServerComm.ReadMsgData(var Msg: TMsg); Var CDS : PCopyDataStruct; begin CDS:=PCopyDataStruct(Msg.Lparam); Owner.FMsgData.Seek(0,soFrombeginning); Owner.FMsgData.WriteBuffer(CDS^.lpData^,CDS^.cbData); end; procedure TWinMsgServerComm.ReadMessage; Var Msg : TMsg; begin If FDataPushed then FDataPushed:=False else If Windows.PeekMessage(Msg, FHWND, 0, 0, PM_REMOVE) then if (Msg.Message=WM_COPYDATA) then ReadMsgData(Msg); end; function TWinMsgServerComm.GetInstanceID: String; begin Result:=IntToStr(HInstance); end; { --------------------------------------------------------------------- TWinMsgClientComm ---------------------------------------------------------------------} Type TWinMsgClientComm = Class(TIPCClientComm) Private FWindowName: String; FHWND : HWnd; Public Constructor Create(AOWner : TSimpleIPCClient); override; Procedure Connect; override; Procedure Disconnect; override; Procedure SendMessage(MsgType : TMessageType; Stream : TStream); override; Function ServerRunning : Boolean; override; Property WindowName : String Read FWindowName; end; constructor TWinMsgClientComm.Create(AOWner: TSimpleIPCClient); begin inherited Create(AOWner); FWindowName:=Owner.ServerID; If (Owner.ServerInstance<>'') then FWindowName:=FWindowName+'_'+Owner.ServerInstance; end; procedure TWinMsgClientComm.Connect; begin FHWND:=FindWindow(MsgWndClassName,Pwidechar(FWindowName)); If (FHWND=0) then Owner.DoError(SErrServerNotActive,[Owner.ServerID]); end; procedure TWinMsgClientComm.Disconnect; begin FHWND:=0; end; procedure TWinMsgClientComm.SendMessage(MsgType: TMessageType; Stream: TStream ); Var CDS : TCopyDataStruct; Data,FMemstr : TMemorySTream; begin If Stream is TMemoryStream then begin Data:=TMemoryStream(Stream); FMemStr:=Nil end else begin FMemStr:=TMemoryStream.Create; Data:=FMemstr; end; Try If Assigned(FMemStr) then begin FMemStr.CopyFrom(Stream,0); FMemStr.Seek(0,soFromBeginning); end; CDS.lpData:=Data.Memory; CDS.cbData:=Data.Size; Windows.SendMessage(FHWnd,WM_COPYDATA,0,Integer(@CDS)); Finally FreeAndNil(FMemStr); end; end; function TWinMsgClientComm.ServerRunning: Boolean; begin Result:=FindWindow(MsgWndClassName,PWidechar(FWindowName))<>0; end; { --------------------------------------------------------------------- Set TSimpleIPCClient / TSimpleIPCServer defaults. ---------------------------------------------------------------------} Function TSimpleIPCServer.CommClass : TIPCServerCommClass; begin if (DefaultIPCServerClass<>Nil) then Result:=DefaultIPCServerClass else Result:=TWinMsgServerComm; end; Function TSimpleIPCClient.CommClass : TIPCClientCommClass; begin if (DefaultIPCClientClass<>Nil) then Result:=DefaultIPCClientClass else Result:=TWinMsgClientComm; end;