SOUI新组件SIpcObject介绍

时间:2023-12-29 12:27:44

SIpcObject是一个基于Windows消息及共享内存的一个IPC(跨进程函数调用)的组件。

GITHUB上有很多IPC模块,我这里又造了一个*,不一定比现有的IPC更好,不过我觉得已经足够简单了。

老规矩,先看一下IPC模块的路径:

SOUI新组件SIpcObject介绍

再看一下IPC模块的接口:

 #pragma once

 #include <unknown/obj-ref-i.h>

 #define UM_CALL_FUN (WM_USER+1000)

 namespace SOUI
{
enum {
FUN_ID_CONNECT = ,
FUN_ID_DISCONNECT,
FUN_ID_START,
}; struct IShareBuffer {
virtual void StartRead() = ;
virtual void StartWrite() = ;
virtual int Write(const void * data, UINT nLen) = ;
virtual int Read(void * buf, UINT nLen) = ;
}; class SParamStream
{
public:
SParamStream(IShareBuffer *pBuf, bool bOutStream) :m_pBuffer(pBuf)
{
m_pBuffer->StartRead();
if (bOutStream) m_pBuffer->StartWrite();
} IShareBuffer * GetBuffer() {
return m_pBuffer;
} template<typename T>
SParamStream & operator<<(const T & data)
{
Write((const void*)&data, sizeof(data));
return *this;
} template<typename T>
SParamStream & operator >> (T &data)
{
Read((void*)&data, sizeof(data));
return *this;
} public:
int Write(const void * data, int nLen)
{
return m_pBuffer->Write(data, nLen);
}
int Read(void * buf, int nLen)
{
return m_pBuffer->Read(buf, nLen);
} protected:
IShareBuffer * m_pBuffer;
}; struct IFunParams
{
virtual UINT GetID() = ;
virtual void ToStream4Input(SParamStream & ps) = ;
virtual void ToStream4Output(SParamStream & ps) = ;
virtual void FromStream4Input(SParamStream & ps) = ;
virtual void FromStream4Output(SParamStream & ps) = ;
}; struct IIpcConnection;
struct IIpcHandle : IObjRef
{
virtual void SetIpcConnection(IIpcConnection *pConn) = ; virtual IIpcConnection * GetIpcConnection() const = ; virtual LRESULT OnMessage(ULONG_PTR idLocal, UINT uMsg, WPARAM wp, LPARAM lp, BOOL &bHandled) = ; virtual HRESULT ConnectTo(ULONG_PTR idLocal, ULONG_PTR idRemote) = ; virtual HRESULT Disconnect() = ; virtual bool CallFun(IFunParams * pParam) const = ; virtual ULONG_PTR GetLocalId() const = ; virtual ULONG_PTR GetRemoteId() const = ; virtual IShareBuffer * GetSendBuffer() = ; virtual IShareBuffer * GetRecvBuffer() = ; virtual BOOL InitShareBuf(ULONG_PTR idLocal, ULONG_PTR idRemote, UINT nBufSize, void* pSa) = ;
}; struct IIpcConnection : IObjRef
{
virtual IIpcHandle * GetIpcHandle() = ;
virtual bool HandleFun(UINT uFunID, SParamStream & ps) = ;
virtual void BuildShareBufferName(ULONG_PTR idLocal, ULONG_PTR idRemote, TCHAR szBuf[MAX_PATH]) const = ;
}; struct IIpcSvrCallback
{
virtual void OnNewConnection(IIpcHandle * pIpcHandle, IIpcConnection ** ppConn) = ;
virtual int GetBufSize() const = ;
virtual void * GetSecurityAttr() const = ;
virtual void ReleaseSecurityAttr(void* psa) const = ;
}; struct IIpcServer : IObjRef
{
virtual HRESULT Init(ULONG_PTR idSvr, IIpcSvrCallback * pCallback) =;
virtual void CheckConnectivity() =;
virtual LRESULT OnMessage(ULONG_PTR idLocal, UINT uMsg, WPARAM wp, LPARAM lp,BOOL &bHandled) =;
}; struct IIpcFactory : IObjRef
{
virtual HRESULT CreateIpcServer(IIpcServer ** ppServer) =;
virtual HRESULT CreateIpcHandle(IIpcHandle ** ppHandle) =;
}; }

和所有SOUI的组件一样,可以通过SOUI::IPC::SCreateInstance来创建IPC组件的IIpcFactory接口。

有了这个接口就可以用来创建IIpcServer和IIpcHandle这两个对象了。

IIpcServer是在IPC的服务端运行的接口,IIpcHandle是用来在服务端和客户端通讯的接口,在服务端,IIpcHandle由IIpcServer在客户端发起连接请求时自动创建,在客户端则直接使用IIpcFactory创建。

IIpcHandle是由SIpcObject实现的,在应用层中只需要直接使用。

应用层为了实现客户端与服务器的通讯还需要定义好协议。

SIpcObject的协议就是一个继承自IFunParam接口的定义的调用方法ID及方法参数。

下面看一下启程输入法使用IpcObject的协议定义。

 #pragma once
#include <string>
#include <sstream>
#include "sinstar-i.h"
#include "TextService-i.h"
#include <interface/SIpcObj-i.h>
#include <helper/sipcparamhelper.hpp> #define SINSTAR3_SERVER_HWND _T("sinstar3_server_wnd_{85B55CBC-7D48-4860-BA88-0BE4B073A94F}")
#define SINSTAR3_SHARE_BUF_NAME_FMT _T("sistart3_share_buffer_8085395F-E2FA-4F96-8BD0-FE5D7412CD22_%08x_2_%08x") //////////////////////////////////////////////////////////////////
namespace SOUI{ template<>
inline SParamStream & SParamStream::operator<<(const std::string & str)
{
int nSize = (int)str.size();
GetBuffer()->Write((const BYTE*)&nSize, sizeof(int));
GetBuffer()->Write((const BYTE*)str.c_str(), nSize);
return *this;
}
template<>
inline SParamStream & SParamStream::operator >> (std::string & str)
{
int nSize = ;
GetBuffer()->Read((BYTE*)&nSize, sizeof(int));
char *pBuf = new char[nSize];
GetBuffer()->Read((BYTE*)pBuf, nSize);
str = std::string(pBuf, nSize);
delete[]pBuf;
return *this;
} ////////////////////////////////////////////////////////////////////////
template<>
inline SParamStream & SParamStream::operator<<(const std::wstring & str)
{
int nSize = (int)str.size();
GetBuffer()->Write((const BYTE*)&nSize, sizeof(int));
GetBuffer()->Write((const BYTE*)str.c_str(), nSize*sizeof(wchar_t));
return *this;
}
template<>
inline SParamStream & SParamStream::operator >> (std::wstring & str)
{
int nSize = ;
GetBuffer()->Read((BYTE*)&nSize, sizeof(int));
wchar_t *pBuf = new wchar_t[nSize];
GetBuffer()->Read((BYTE*)pBuf, nSize*sizeof(wchar_t));
str = std::wstring(pBuf, nSize);
delete[]pBuf;
return *this;
} //////////////////////////////////////////////////////////////////////
template<>
inline SParamStream & SParamStream::operator<<(const POINT & pt)
{
GetBuffer()->Write((const BYTE*)&pt.x, sizeof(int));
GetBuffer()->Write((const BYTE*)&pt.y, sizeof(int));
return *this;
}
template<>
inline SParamStream & SParamStream::operator >> (POINT & pt)
{
int tmp = ;
GetBuffer()->Read((BYTE*)&tmp, sizeof(int));
pt.x = tmp;
GetBuffer()->Read((BYTE*)&tmp, sizeof(int));
pt.y = tmp;
return *this;
} } struct FunParams_Base : SOUI::IFunParams
{
virtual void ToStream4Input(SOUI::SParamStream & ps) {}
virtual void ToStream4Output(SOUI::SParamStream & ps) {}
virtual void FromStream4Input(SOUI::SParamStream & ps) {}
virtual void FromStream4Output(SOUI::SParamStream & ps) {}
}; enum {
ISinstar_Create = SOUI::FUN_ID_START,
ISinstar_Destroy,
ISinstar_OnImeSelect,
ISinstar_OnCompositionStarted,
ISinstar_OnCompositionChanged,
ISinstar_OnCompositionTerminated,
ISinstar_OnSetCaretPosition,
ISinstar_OnSetFocusSegmentPosition,
ISinstar_ProcessKeyStoke,
ISinstar_TranslateKey,
ISinstar_OnSetFocus,
ISinstar_GetCompositionSegments,
ISinstar_GetCompositionSegmentEnd,
ISinstar_GetCompositionSegmentAttr,
ISinstar_OnOpenStatusChanged,
ISinstar_OnConversionModeChanged,
ISinstar_ShowHelp,
ISinstar_GetDefInputMode, ITextService_InputStringW = ISinstar_GetDefInputMode + ,
ITextService_IsCompositing,
ITextService_StartComposition,
ITextService_ReplaceSelCompositionW,
ITextService_UpdateResultAndCompositionStringW,
ITextService_EndComposition,
ITextService_GetImeContext,
ITextService_ReleaseImeContext,
ITextService_SetConversionMode,
ITextService_GetConversionMode,
ITextService_SetOpenStatus,
ITextService_GetOpenStatus,
ITextService_GetActiveWnd,
}; struct Param_Create : FunParams_Base
{
bool bDpiAware;
std::string strHostPath;
DWORD dwVer;
FUNID(ISinstar_Create)
PARAMS3(Input, bDpiAware,strHostPath,dwVer)
}; struct Param_Destroy : FunParams_Base
{
FUNID(ISinstar_Destroy)
}; struct Param_OnImeSelect : FunParams_Base
{
BOOL bSelect;
FUNID(ISinstar_OnImeSelect)
PARAMS1(Input, bSelect)
}; struct Param_OnCompositionStarted : FunParams_Base
{
FUNID(ISinstar_OnCompositionStarted)
}; struct Param_OnCompositionTerminated : FunParams_Base
{
bool bClearCtx;
FUNID(ISinstar_OnCompositionTerminated)
PARAMS1(Input, bClearCtx)
}; struct Param_OnCompositionChanged : FunParams_Base
{
FUNID(ISinstar_OnCompositionChanged)
}; struct Param_OnSetCaretPosition : FunParams_Base
{
POINT pt;
int nHei;
FUNID(ISinstar_OnSetCaretPosition)
PARAMS2(Input, pt,nHei)
}; struct Param_OnSetFocusSegmentPosition : FunParams_Base
{
POINT pt; int nHei;
FUNID(ISinstar_OnSetFocusSegmentPosition)
PARAMS2(Input, pt, nHei)
}; struct Param_ProcessKeyStoke : FunParams_Base {
UINT64 lpImeContext; UINT vkCode; DWORD lParam; BOOL bKeyDown;
BYTE byKeyState[];
BOOL bEaten;
FUNID(ISinstar_ProcessKeyStoke)
PARAMS5(Input, lpImeContext, vkCode, lParam, bKeyDown, byKeyState)
PARAMS1(Output,bEaten)
}; struct Param_TranslateKey : FunParams_Base
{
UINT64 lpImeContext; UINT vkCode; UINT uScanCode; BOOL bKeyDown;
BYTE byKeyState[];
BOOL bEaten;
FUNID(ISinstar_TranslateKey)
PARAMS5(Input, lpImeContext, vkCode, uScanCode, bKeyDown, byKeyState)
PARAMS1(Output, bEaten)
}; struct Param_OnSetFocus : FunParams_Base
{
BOOL bFocus;
FUNID(ISinstar_OnSetFocus)
PARAMS1(Input, bFocus)
}; struct Param_GetCompositionSegments : FunParams_Base
{
int nSegs;
FUNID(ISinstar_GetCompositionSegments)
PARAMS1(Output, nSegs)
}; struct Param_GetCompositionSegmentEnd : FunParams_Base
{
int iSeg;
int iEnd;
FUNID(ISinstar_GetCompositionSegmentEnd)
PARAMS1(Input,iSeg)
PARAMS1(Output,iEnd)
}; struct Param_GetCompositionSegmentAttr : FunParams_Base
{
int iSeg;
int nAttr;
FUNID(ISinstar_GetCompositionSegmentAttr)
PARAMS1(Input, iSeg)
PARAMS1(Output, nAttr)
}; struct Param_OnOpenStatusChanged : FunParams_Base
{
BOOL bOpen;
FUNID(ISinstar_OnOpenStatusChanged)
PARAMS1(Input, bOpen)
}; struct Param_OnConversionModeChanged : FunParams_Base
{
EInputMethod uMode;
FUNID(ISinstar_OnConversionModeChanged)
PARAMS1(Input, uMode)
}; struct Param_ShowHelp : FunParams_Base
{
FUNID(ISinstar_ShowHelp)
}; struct Param_GetDefInputMode : FunParams_Base
{
EInputMethod uMode;
FUNID(ISinstar_GetDefInputMode)
PARAMS1(Output,uMode)
}; ////////////////////////////////////////////////////////////////////////////
struct Param_InputStringW : FunParams_Base
{
std::wstring buf;
BOOL bRet;
FUNID(ITextService_InputStringW)
PARAMS1(Input,buf)
PARAMS1(Output,bRet)
}; struct Param_IsCompositing : FunParams_Base
{
BOOL bRet;
FUNID(ITextService_IsCompositing)
PARAMS1(Output,bRet)
}; struct Param_StartComposition : FunParams_Base
{
UINT64 lpImeContext;
FUNID(ITextService_StartComposition)
PARAMS1(Input,lpImeContext)
}; struct Param_ReplaceSelCompositionW : FunParams_Base
{
UINT64 lpImeContext; int nLeft; int nRight; std::wstring buf;
FUNID(ITextService_ReplaceSelCompositionW)
PARAMS4(Input,lpImeContext,nLeft,nRight,buf)
}; struct Param_UpdateResultAndCompositionStringW : FunParams_Base
{
UINT64 lpImeContext; std::wstring resultStr; std::wstring compStr;
FUNID(ITextService_UpdateResultAndCompositionStringW)
PARAMS3(Input, lpImeContext, resultStr, compStr)
}; struct Param_EndComposition : FunParams_Base
{
UINT64 lpImeContext;
FUNID(ITextService_EndComposition)
PARAMS1(Input,lpImeContext)
}; struct Param_GetImeContext : FunParams_Base
{
UINT64 lpImeContext;
FUNID(ITextService_GetImeContext)
PARAMS1(Output,lpImeContext)
}; struct Param_ReleaseImeContext : FunParams_Base
{
UINT64 lpImeContext;
BOOL bRet;
FUNID(ITextService_ReleaseImeContext)
PARAMS1(Input, lpImeContext)
PARAMS1(Output,bRet)
}; struct Param_SetConversionMode : FunParams_Base
{
EInputMethod mode;
FUNID(ITextService_SetConversionMode)
PARAMS1(Input,mode)
}; struct Param_GetConversionMode : FunParams_Base
{
EInputMethod mode;
FUNID(ITextService_GetConversionMode)
PARAMS1(Output, mode)
}; struct Param_SetOpenStatus : FunParams_Base
{
UINT64 lpImeContext;
BOOL bOpen;
BOOL bRet;
FUNID(ITextService_SetOpenStatus)
PARAMS2(Input,lpImeContext,bOpen)
PARAMS1(Output,bRet)
}; struct Param_GetOpenStatus : FunParams_Base
{
UINT64 lpImeContext;
BOOL bOpen;
FUNID(ITextService_GetOpenStatus)
PARAMS1(Input, lpImeContext)
PARAMS1(Output, bOpen)
}; struct Param_GetActiveWnd : FunParams_Base
{
DWORD hActive;
FUNID(ITextService_GetActiveWnd)
PARAMS1(Output, hActive)
}
首先我们通过一组枚举值定义所有调用的函数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调用。

 class CClientConnection : public SOUI::TObjRefImpl<SOUI::IIpcConnection>
{
public:
CClientConnection(ITextService * pTxtService); public:
// 通过 IIpcConnection 继承
virtual SOUI::IIpcHandle * GetIpcHandle() override;
virtual void BuildShareBufferName(ULONG_PTR idLocal, ULONG_PTR idRemote, TCHAR szName[MAX_PATH]) const override;
bool CallFun(SOUI::IFunParams *params) const;
protected:
void OnInputStringW( Param_InputStringW &param);
void OnIsCompositing( Param_IsCompositing &param);
void OnStartComposition( Param_StartComposition &param);
void OnReplaceSelCompositionW( Param_ReplaceSelCompositionW &param);
void OnUpdateResultAndCompositionStringW( Param_UpdateResultAndCompositionStringW &param);
void OnEndComposition( Param_EndComposition &param);
void OnGetImeContext( Param_GetImeContext &param);
void OnReleaseImeContext( Param_ReleaseImeContext &param);
void OnSetConversionMode( Param_SetConversionMode &param);
void OnGetConversionMode( Param_GetConversionMode &param);
void OnSetOpenStatus( Param_SetOpenStatus &param);
void OnGetOpenStatus( Param_GetOpenStatus &param);
void OnGetActiveWnd( Param_GetActiveWnd &param); FUN_BEGIN
FUN_HANDLER(Param_InputStringW, OnInputStringW)
FUN_HANDLER(Param_IsCompositing, OnIsCompositing)
FUN_HANDLER(Param_StartComposition, OnStartComposition)
FUN_HANDLER(Param_ReplaceSelCompositionW, OnReplaceSelCompositionW)
FUN_HANDLER(Param_UpdateResultAndCompositionStringW, OnUpdateResultAndCompositionStringW)
FUN_HANDLER(Param_EndComposition, OnEndComposition)
FUN_HANDLER(Param_GetImeContext, OnGetImeContext)
FUN_HANDLER(Param_ReleaseImeContext, OnReleaseImeContext)
FUN_HANDLER(Param_SetConversionMode, OnSetConversionMode)
FUN_HANDLER(Param_GetConversionMode, OnGetConversionMode)
FUN_HANDLER(Param_SetOpenStatus, OnSetOpenStatus)
FUN_HANDLER(Param_GetOpenStatus, OnGetOpenStatus)
FUN_HANDLER(Param_GetActiveWnd, OnGetActiveWnd)
FUN_END private:
ITextService * m_pTxtService;
SOUI::CAutoRefPtr<SOUI::IIpcHandle> m_ipcHandle;
};
 bool CClientConnection::CallFun(SOUI::IFunParams *params) const
{
SASSERT(m_ipcHandle);
return m_ipcHandle->CallFun(params);
}
 void CSinstarProxy::ProcessKeyStoke(UINT64 imeContext, UINT vkCode, LPARAM lParam, BOOL bKeyDown, BYTE byKeyState[], BOOL * pbEaten)
{
Param_ProcessKeyStoke param;
param.lpImeContext = imeContext;
param.vkCode = vkCode;
param.lParam = (DWORD)lParam;
param.bKeyDown = bKeyDown;
memcpy(param.byKeyState, byKeyState, );
param.bEaten = false;
m_conn.CallFun(&param);
*pbEaten = param.bEaten;
}
CSinstarProxy对象有一个CClientConnection对象:m_conn,它需要调用服务器的方法ProcessKeyStoke,我们需要把对应的函数参数包装到对象:Param_ProcessKeyStoke中,调用m_conn.CallFun(&param),再从参数中获取返回值。

在CClientConnection对象中有一组FUN_BEGIN,FUN_END包装的处理函数映射表,分别用来处理服务端对客户端的函数调用。

如此,一个客户端服务器双向调用的IPC就完成了。

这个IPC核心就是用参数对象来包装参数列表并经过序列化,反序列化来实现跨进程函数调用,并通过实现一些宏简化开发,美化代码结构,目前在我的启程输入法3.0中工作很好。

启程输入法3.0 GIT仓库: https://gitee.com/setoutsoft/sinstar3

启程软件 2019-02-03