SIpcObject是一个基于Windows消息及共享内存的一个IPC(跨进程函数调用)的组件。
GITHUB上有很多IPC模块,我这里又造了一个轮子,不一定比现有的IPC更好,不过我觉得已经足够简单了。
老规矩,先看一下IPC模块的路径:
再看一下IPC模块的接口:
1 #pragma once 2 3 #include <unknown/obj-ref-i.h> 4 5 #define UM_CALL_FUN (WM_USER+1000) 6 7 namespace SOUI 8 { 9 enum { 10 FUN_ID_CONNECT = 100, 11 FUN_ID_DISCONNECT, 12 FUN_ID_START, 13 }; 14 15 struct IShareBuffer { 16 virtual void StartRead() = 0; 17 virtual void StartWrite() = 0; 18 virtual int Write(const void * data, UINT nLen) = 0; 19 virtual int Read(void * buf, UINT nLen) = 0; 20 }; 21 22 23 class SParamStream 24 { 25 public: 26 SParamStream(IShareBuffer *pBuf, bool bOutStream) :m_pBuffer(pBuf) 27 { 28 m_pBuffer->StartRead(); 29 if (bOutStream) m_pBuffer->StartWrite(); 30 } 31 32 IShareBuffer * GetBuffer() { 33 return m_pBuffer; 34 } 35 36 template<typename T> 37 SParamStream & operator<<(const T & data) 38 { 39 Write((const void*)&data, sizeof(data)); 40 return *this; 41 } 42 43 44 template<typename T> 45 SParamStream & operator >> (T &data) 46 { 47 Read((void*)&data, sizeof(data)); 48 return *this; 49 } 50 51 public: 52 int Write(const void * data, int nLen) 53 { 54 return m_pBuffer->Write(data, nLen); 55 } 56 int Read(void * buf, int nLen) 57 { 58 return m_pBuffer->Read(buf, nLen); 59 } 60 61 protected: 62 IShareBuffer * m_pBuffer; 63 }; 64 65 struct IFunParams 66 { 67 virtual UINT GetID() = 0; 68 virtual void ToStream4Input(SParamStream & ps) = 0; 69 virtual void ToStream4Output(SParamStream & ps) = 0; 70 virtual void FromStream4Input(SParamStream & ps) = 0; 71 virtual void FromStream4Output(SParamStream & ps) = 0; 72 }; 73 74 struct IIpcConnection; 75 struct IIpcHandle : IObjRef 76 { 77 virtual void SetIpcConnection(IIpcConnection *pConn) = 0; 78 79 virtual IIpcConnection * GetIpcConnection() const = 0; 80 81 virtual LRESULT OnMessage(ULONG_PTR idLocal, UINT uMsg, WPARAM wp, LPARAM lp, BOOL &bHandled) = 0; 82 83 virtual HRESULT ConnectTo(ULONG_PTR idLocal, ULONG_PTR idRemote) = 0; 84 85 virtual HRESULT Disconnect() = 0; 86 87 virtual bool CallFun(IFunParams * pParam) const = 0; 88 89 virtual ULONG_PTR GetLocalId() const = 0; 90 91 virtual ULONG_PTR GetRemoteId() const = 0; 92 93 virtual IShareBuffer * GetSendBuffer() = 0; 94 95 virtual IShareBuffer * GetRecvBuffer() = 0; 96 97 virtual BOOL InitShareBuf(ULONG_PTR idLocal, ULONG_PTR idRemote, UINT nBufSize, void* pSa) = 0; 98 }; 99 100 struct IIpcConnection : IObjRef 101 { 102 virtual IIpcHandle * GetIpcHandle() = 0; 103 virtual bool HandleFun(UINT uFunID, SParamStream & ps) = 0; 104 virtual void BuildShareBufferName(ULONG_PTR idLocal, ULONG_PTR idRemote, TCHAR szBuf[MAX_PATH]) const = 0; 105 }; 106 107 struct IIpcSvrCallback 108 { 109 virtual void OnNewConnection(IIpcHandle * pIpcHandle, IIpcConnection ** ppConn) = 0; 110 virtual int GetBufSize() const = 0; 111 virtual void * GetSecurityAttr() const = 0; 112 virtual void ReleaseSecurityAttr(void* psa) const = 0; 113 }; 114 115 struct IIpcServer : IObjRef 116 { 117 virtual HRESULT Init(ULONG_PTR idSvr, IIpcSvrCallback * pCallback) =0; 118 virtual void CheckConnectivity() =0; 119 virtual LRESULT OnMessage(ULONG_PTR idLocal, UINT uMsg, WPARAM wp, LPARAM lp,BOOL &bHandled) =0; 120 }; 121 122 struct IIpcFactory : IObjRef 123 { 124 virtual HRESULT CreateIpcServer(IIpcServer ** ppServer) =0; 125 virtual HRESULT CreateIpcHandle(IIpcHandle ** ppHandle) =0; 126 }; 127 128 129 }
和所有SOUI的组件一样,可以通过SOUI::IPC::SCreateInstance来创建IPC组件的IIpcFactory接口。
有了这个接口就可以用来创建IIpcServer和IIpcHandle这两个对象了。
IIpcServer是在IPC的服务端运行的接口,IIpcHandle是用来在服务端和客户端通讯的接口,在服务端,IIpcHandle由IIpcServer在客户端发起连接请求时自动创建,在客户端则直接使用IIpcFactory创建。
IIpcHandle是由SIpcObject实现的,在应用层中只需要直接使用。
应用层为了实现客户端与服务器的通讯还需要定义好协议。
SIpcObject的协议就是一个继承自IFunParam接口的定义的调用方法ID及方法参数。
下面看一下启程输入法使用IpcObject的协议定义。
1 #pragma once 2 #include <string> 3 #include <sstream> 4 #include "sinstar-i.h" 5 #include "TextService-i.h" 6 #include <interface/SIpcObj-i.h> 7 #include <helper/sipcparamhelper.hpp> 8 9 #define SINSTAR3_SERVER_HWND _T("sinstar3_server_wnd_{85B55CBC-7D48-4860-BA88-0BE4B073A94F}") 10 #define SINSTAR3_SHARE_BUF_NAME_FMT _T("sistart3_share_buffer_8085395F-E2FA-4F96-8BD0-FE5D7412CD22_%08x_2_%08x") 11 12 13 ////////////////////////////////////////////////////////////////// 14 namespace SOUI{ 15 16 template<> 17 inline SParamStream & SParamStream::operator<<(const std::string & str) 18 { 19 int nSize = (int)str.size(); 20 GetBuffer()->Write((const BYTE*)&nSize, sizeof(int)); 21 GetBuffer()->Write((const BYTE*)str.c_str(), nSize); 22 return *this; 23 } 24 template<> 25 inline SParamStream & SParamStream::operator >> (std::string & str) 26 { 27 int nSize = 0; 28 GetBuffer()->Read((BYTE*)&nSize, sizeof(int)); 29 char *pBuf = new char[nSize]; 30 GetBuffer()->Read((BYTE*)pBuf, nSize); 31 str = std::string(pBuf, nSize); 32 delete[]pBuf; 33 return *this; 34 } 35 36 //////////////////////////////////////////////////////////////////////// 37 template<> 38 inline SParamStream & SParamStream::operator<<(const std::wstring & str) 39 { 40 int nSize = (int)str.size(); 41 GetBuffer()->Write((const BYTE*)&nSize, sizeof(int)); 42 GetBuffer()->Write((const BYTE*)str.c_str(), nSize*sizeof(wchar_t)); 43 return *this; 44 } 45 template<> 46 inline SParamStream & SParamStream::operator >> (std::wstring & str) 47 { 48 int nSize = 0; 49 GetBuffer()->Read((BYTE*)&nSize, sizeof(int)); 50 wchar_t *pBuf = new wchar_t[nSize]; 51 GetBuffer()->Read((BYTE*)pBuf, nSize*sizeof(wchar_t)); 52 str = std::wstring(pBuf, nSize); 53 delete[]pBuf; 54 return *this; 55 } 56 57 ////////////////////////////////////////////////////////////////////// 58 template<> 59 inline SParamStream & SParamStream::operator<<(const POINT & pt) 60 { 61 GetBuffer()->Write((const BYTE*)&pt.x, sizeof(int)); 62 GetBuffer()->Write((const BYTE*)&pt.y, sizeof(int)); 63 return *this; 64 } 65 template<> 66 inline SParamStream & SParamStream::operator >> (POINT & pt) 67 { 68 int tmp = 0; 69 GetBuffer()->Read((BYTE*)&tmp, sizeof(int)); 70 pt.x = tmp; 71 GetBuffer()->Read((BYTE*)&tmp, sizeof(int)); 72 pt.y = tmp; 73 return *this; 74 } 75 76 } 77 78 struct FunParams_Base : SOUI::IFunParams 79 { 80 virtual void ToStream4Input(SOUI::SParamStream & ps) {} 81 virtual void ToStream4Output(SOUI::SParamStream & ps) {} 82 virtual void FromStream4Input(SOUI::SParamStream & ps) {} 83 virtual void FromStream4Output(SOUI::SParamStream & ps) {} 84 }; 85 86 87 enum { 88 ISinstar_Create = SOUI::FUN_ID_START, 89 ISinstar_Destroy, 90 ISinstar_OnImeSelect, 91 ISinstar_OnCompositionStarted, 92 ISinstar_OnCompositionChanged, 93 ISinstar_OnCompositionTerminated, 94 ISinstar_OnSetCaretPosition, 95 ISinstar_OnSetFocusSegmentPosition, 96 ISinstar_ProcessKeyStoke, 97 ISinstar_TranslateKey, 98 ISinstar_OnSetFocus, 99 ISinstar_GetCompositionSegments, 100 ISinstar_GetCompositionSegmentEnd, 101 ISinstar_GetCompositionSegmentAttr, 102 ISinstar_OnOpenStatusChanged, 103 ISinstar_OnConversionModeChanged, 104 ISinstar_ShowHelp, 105 ISinstar_GetDefInputMode, 106 107 ITextService_InputStringW = ISinstar_GetDefInputMode + 100, 108 ITextService_IsCompositing, 109 ITextService_StartComposition, 110 ITextService_ReplaceSelCompositionW, 111 ITextService_UpdateResultAndCompositionStringW, 112 ITextService_EndComposition, 113 ITextService_GetImeContext, 114 ITextService_ReleaseImeContext, 115 ITextService_SetConversionMode, 116 ITextService_GetConversionMode, 117 ITextService_SetOpenStatus, 118 ITextService_GetOpenStatus, 119 ITextService_GetActiveWnd, 120 }; 121 122 123 struct Param_Create : FunParams_Base 124 { 125 bool bDpiAware; 126 std::string strHostPath; 127 DWORD dwVer; 128 FUNID(ISinstar_Create) 129 PARAMS3(Input, bDpiAware,strHostPath,dwVer) 130 }; 131 132 struct Param_Destroy : FunParams_Base 133 { 134 FUNID(ISinstar_Destroy) 135 }; 136 137 struct Param_OnImeSelect : FunParams_Base 138 { 139 BOOL bSelect; 140 FUNID(ISinstar_OnImeSelect) 141 PARAMS1(Input, bSelect) 142 }; 143 144 struct Param_OnCompositionStarted : FunParams_Base 145 { 146 FUNID(ISinstar_OnCompositionStarted) 147 }; 148 149 150 struct Param_OnCompositionTerminated : FunParams_Base 151 { 152 bool bClearCtx; 153 FUNID(ISinstar_OnCompositionTerminated) 154 PARAMS1(Input, bClearCtx) 155 }; 156 157 struct Param_OnCompositionChanged : FunParams_Base 158 { 159 FUNID(ISinstar_OnCompositionChanged) 160 }; 161 162 struct Param_OnSetCaretPosition : FunParams_Base 163 { 164 POINT pt; 165 int nHei; 166 FUNID(ISinstar_OnSetCaretPosition) 167 PARAMS2(Input, pt,nHei) 168 }; 169 170 struct Param_OnSetFocusSegmentPosition : FunParams_Base 171 { 172 POINT pt; int nHei; 173 FUNID(ISinstar_OnSetFocusSegmentPosition) 174 PARAMS2(Input, pt, nHei) 175 }; 176 177 struct Param_ProcessKeyStoke : FunParams_Base { 178 UINT64 lpImeContext; UINT vkCode; DWORD lParam; BOOL bKeyDown; 179 BYTE byKeyState[256]; 180 BOOL bEaten; 181 FUNID(ISinstar_ProcessKeyStoke) 182 PARAMS5(Input, lpImeContext, vkCode, lParam, bKeyDown, byKeyState) 183 PARAMS1(Output,bEaten) 184 }; 185 186 struct Param_TranslateKey : FunParams_Base 187 { 188 UINT64 lpImeContext; UINT vkCode; UINT uScanCode; BOOL bKeyDown; 189 BYTE byKeyState[256]; 190 BOOL bEaten; 191 FUNID(ISinstar_TranslateKey) 192 PARAMS5(Input, lpImeContext, vkCode, uScanCode, bKeyDown, byKeyState) 193 PARAMS1(Output, bEaten) 194 }; 195 196 struct Param_OnSetFocus : FunParams_Base 197 { 198 BOOL bFocus; 199 FUNID(ISinstar_OnSetFocus) 200 PARAMS1(Input, bFocus) 201 }; 202 203 struct Param_GetCompositionSegments : FunParams_Base 204 { 205 int nSegs; 206 FUNID(ISinstar_GetCompositionSegments) 207 PARAMS1(Output, nSegs) 208 }; 209 210 struct Param_GetCompositionSegmentEnd : FunParams_Base 211 { 212 int iSeg; 213 int iEnd; 214 FUNID(ISinstar_GetCompositionSegmentEnd) 215 PARAMS1(Input,iSeg) 216 PARAMS1(Output,iEnd) 217 }; 218 219 struct Param_GetCompositionSegmentAttr : FunParams_Base 220 { 221 int iSeg; 222 int nAttr; 223 FUNID(ISinstar_GetCompositionSegmentAttr) 224 PARAMS1(Input, iSeg) 225 PARAMS1(Output, nAttr) 226 }; 227 228 struct Param_OnOpenStatusChanged : FunParams_Base 229 { 230 BOOL bOpen; 231 FUNID(ISinstar_OnOpenStatusChanged) 232 PARAMS1(Input, bOpen) 233 }; 234 235 struct Param_OnConversionModeChanged : FunParams_Base 236 { 237 EInputMethod uMode; 238 FUNID(ISinstar_OnConversionModeChanged) 239 PARAMS1(Input, uMode) 240 }; 241 242 struct Param_ShowHelp : FunParams_Base 243 { 244 FUNID(ISinstar_ShowHelp) 245 }; 246 247 struct Param_GetDefInputMode : FunParams_Base 248 { 249 EInputMethod uMode; 250 FUNID(ISinstar_GetDefInputMode) 251 PARAMS1(Output,uMode) 252 }; 253 254 255 //////////////////////////////////////////////////////////////////////////// 256 struct Param_InputStringW : FunParams_Base 257 { 258 std::wstring buf; 259 BOOL bRet; 260 FUNID(ITextService_InputStringW) 261 PARAMS1(Input,buf) 262 PARAMS1(Output,bRet) 263 }; 264 265 struct Param_IsCompositing : FunParams_Base 266 { 267 BOOL bRet; 268 FUNID(ITextService_IsCompositing) 269 PARAMS1(Output,bRet) 270 }; 271 272 struct Param_StartComposition : FunParams_Base 273 { 274 UINT64 lpImeContext; 275 FUNID(ITextService_StartComposition) 276 PARAMS1(Input,lpImeContext) 277 }; 278 279 struct Param_ReplaceSelCompositionW : FunParams_Base 280 { 281 UINT64 lpImeContext; int nLeft; int nRight; std::wstring buf; 282 FUNID(ITextService_ReplaceSelCompositionW) 283 PARAMS4(Input,lpImeContext,nLeft,nRight,buf) 284 }; 285 286 struct Param_UpdateResultAndCompositionStringW : FunParams_Base 287 { 288 UINT64 lpImeContext; std::wstring resultStr; std::wstring compStr; 289 FUNID(ITextService_UpdateResultAndCompositionStringW) 290 PARAMS3(Input, lpImeContext, resultStr, compStr) 291 }; 292 293 struct Param_EndComposition : FunParams_Base 294 { 295 UINT64 lpImeContext; 296 FUNID(ITextService_EndComposition) 297 PARAMS1(Input,lpImeContext) 298 }; 299 300 struct Param_GetImeContext : FunParams_Base 301 { 302 UINT64 lpImeContext; 303 FUNID(ITextService_GetImeContext) 304 PARAMS1(Output,lpImeContext) 305 }; 306 307 struct Param_ReleaseImeContext : FunParams_Base 308 { 309 UINT64 lpImeContext; 310 BOOL bRet; 311 FUNID(ITextService_ReleaseImeContext) 312 PARAMS1(Input, lpImeContext) 313 PARAMS1(Output,bRet) 314 }; 315 316 struct Param_SetConversionMode : FunParams_Base 317 { 318 EInputMethod mode; 319 FUNID(ITextService_SetConversionMode) 320 PARAMS1(Input,mode) 321 }; 322 323 struct Param_GetConversionMode : FunParams_Base 324 { 325 EInputMethod mode; 326 FUNID(ITextService_GetConversionMode) 327 PARAMS1(Output, mode) 328 }; 329 330 struct Param_SetOpenStatus : FunParams_Base 331 { 332 UINT64 lpImeContext; 333 BOOL bOpen; 334 BOOL bRet; 335 FUNID(ITextService_SetOpenStatus) 336 PARAMS2(Input,lpImeContext,bOpen) 337 PARAMS1(Output,bRet) 338 }; 339 340 struct Param_GetOpenStatus : FunParams_Base 341 { 342 UINT64 lpImeContext; 343 BOOL bOpen; 344 FUNID(ITextService_GetOpenStatus) 345 PARAMS1(Input, lpImeContext) 346 PARAMS1(Output, bOpen) 347 }; 348 349 struct Param_GetActiveWnd : FunParams_Base 350 { 351 DWORD hActive; 352 FUNID(ITextService_GetActiveWnd) 353 PARAMS1(Output, hActive) 354 }
首先我们通过一组枚举值定义所有调用的函数ID。
然后实现一个继承自IFunParams的对象FunParams_Base,以实现接口中的缺省方法。
然后从FunParams_Base继承出每一个IPC调用需要的参数。
我们以256行的Param_InputStringW为例来说明如何定义方法参数。
struct Param_InputStringW : FunParams_Base { std::wstring buf; BOOL bRet; FUNID(ITextService_InputStringW) PARAMS1(Input,buf) PARAMS1(Output,bRet) };
这个IPC调用输入是一个wstring字符串,输出是一个BOOL类型返回值。
首先在对象中定义这两个成员变量。
定义好后通过宏FUNID来指定这个方法的函数调用ID。
再通过宏PARAM1(Input,buf)来指定这个方法的输入参数buf, 注意宏的第一个参数"input"。
第三步通过宏PARAM1(output,bRet)来定义这个方法的输出变量为bRet. PARAMX目前实现的X范围为1-5, 分别对应1-5个参数,如果在一次调用中有更多参数,可以参考PARAMX的实现多写几个宏就好了。
实际上这些宏就是为了组合IFunParams的几个虚方法。
这个对象在进行IPC调用的时候,先在请求端借助SParamStream对象序列化到共享内存中,SParamStream重载了输入"<<"及输出">>"操作符,默认操作是直接拷贝变量内存,这对于基本变量类型是适用的,但是对于string,wstring等对象就不适用了,对于那些不能通过简单的内存拷贝来传递的对象,我们需要像协议开头那样为这些类型的序列化做模板特化。对于比如POINT这样的对象也是可以直接通过内存拷贝就可以实现序列化的,因此这里对POINT的特化其实是多余的(最新的代码已经删除)。
协议定义好后,我们来看看如何进行IPC调用及响应IPC调用。
1 class CClientConnection : public SOUI::TObjRefImpl<SOUI::IIpcConnection> 2 { 3 public: 4 CClientConnection(ITextService * pTxtService); 5 6 public: 7 // 通过 IIpcConnection 继承 8 virtual SOUI::IIpcHandle * GetIpcHandle() override; 9 virtual void BuildShareBufferName(ULONG_PTR idLocal, ULONG_PTR idRemote, TCHAR szName[MAX_PATH]) const override; 10 bool CallFun(SOUI::IFunParams *params) const; 11 protected: 12 void OnInputStringW( Param_InputStringW ¶m); 13 void OnIsCompositing( Param_IsCompositing ¶m); 14 void OnStartComposition( Param_StartComposition ¶m); 15 void OnReplaceSelCompositionW( Param_ReplaceSelCompositionW ¶m); 16 void OnUpdateResultAndCompositionStringW( Param_UpdateResultAndCompositionStringW ¶m); 17 void OnEndComposition( Param_EndComposition ¶m); 18 void OnGetImeContext( Param_GetImeContext ¶m); 19 void OnReleaseImeContext( Param_ReleaseImeContext ¶m); 20 void OnSetConversionMode( Param_SetConversionMode ¶m); 21 void OnGetConversionMode( Param_GetConversionMode ¶m); 22 void OnSetOpenStatus( Param_SetOpenStatus ¶m); 23 void OnGetOpenStatus( Param_GetOpenStatus ¶m); 24 void OnGetActiveWnd( Param_GetActiveWnd ¶m); 25 26 FUN_BEGIN 27 FUN_HANDLER(Param_InputStringW, OnInputStringW) 28 FUN_HANDLER(Param_IsCompositing, OnIsCompositing) 29 FUN_HANDLER(Param_StartComposition, OnStartComposition) 30 FUN_HANDLER(Param_ReplaceSelCompositionW, OnReplaceSelCompositionW) 31 FUN_HANDLER(Param_UpdateResultAndCompositionStringW, OnUpdateResultAndCompositionStringW) 32 FUN_HANDLER(Param_EndComposition, OnEndComposition) 33 FUN_HANDLER(Param_GetImeContext, OnGetImeContext) 34 FUN_HANDLER(Param_ReleaseImeContext, OnReleaseImeContext) 35 FUN_HANDLER(Param_SetConversionMode, OnSetConversionMode) 36 FUN_HANDLER(Param_GetConversionMode, OnGetConversionMode) 37 FUN_HANDLER(Param_SetOpenStatus, OnSetOpenStatus) 38 FUN_HANDLER(Param_GetOpenStatus, OnGetOpenStatus) 39 FUN_HANDLER(Param_GetActiveWnd, OnGetActiveWnd) 40 FUN_END 41 42 private: 43 ITextService * m_pTxtService; 44 SOUI::CAutoRefPtr<SOUI::IIpcHandle> m_ipcHandle; 45 };
1 bool CClientConnection::CallFun(SOUI::IFunParams *params) const 2 { 3 SASSERT(m_ipcHandle); 4 return m_ipcHandle->CallFun(params); 5 }
1 void CSinstarProxy::ProcessKeyStoke(UINT64 imeContext, UINT vkCode, LPARAM lParam, BOOL bKeyDown, BYTE byKeyState[256], BOOL * pbEaten) 2 { 3 Param_ProcessKeyStoke param; 4 param.lpImeContext = imeContext; 5 param.vkCode = vkCode; 6 param.lParam = (DWORD)lParam; 7 param.bKeyDown = bKeyDown; 8 memcpy(param.byKeyState, byKeyState, 256); 9 param.bEaten = false; 10 m_conn.CallFun(¶m); 11 *pbEaten = param.bEaten; 12 }
CSinstarProxy对象有一个CClientConnection对象:m_conn,它需要调用服务器的方法ProcessKeyStoke,我们需要把对应的函数参数包装到对象:Param_ProcessKeyStoke中,调用m_conn.CallFun(¶m),再从参数中获取返回值。
在CClientConnection对象中有一组FUN_BEGIN,FUN_END包装的处理函数映射表,分别用来处理服务端对客户端的函数调用。
如此,一个客户端服务器双向调用的IPC就完成了。
这个IPC核心就是用参数对象来包装参数列表并经过序列化,反序列化来实现跨进程函数调用,并通过实现一些宏简化开发,美化代码结构,目前在我的启程输入法3.0中工作很好。
启程输入法3.0 GIT仓库: https://gitee.com/setoutsoft/sinstar3
启程软件 2019-02-03