智能家居通用管理平台 - 进程间通信
Posted ionfox
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能家居通用管理平台 - 进程间通信相关的知识,希望对你有一定的参考价值。
如果设备系统(如虚拟的音乐系统),运行在服务器电脑上,那么使用IPC通信是较好的办法,即使没有网络, SHP仍然可以与设备系统交互。如何设计进程间通信,需要考虑几个方面的事宜。
一是通信的频率问题。有些数据很少需要交互。而设备的状态数据的传送,有突发性,某些时段,发送频率比较高,因此可能需要考虑使用多种通信方式。
二是数据的丢失问题。当通信繁忙时,如果频繁触发报警条件,SHS需要时间去处理任务(目前是使用线程去处理)。要确保设备系统传来的信息不丢失,也就是说要有存储信息的功能。
鉴于此,我们设计使用共享内存来进行进程间基本信息的交互,因为这些信息更新的频率很低,如进程退出、窗体隐藏等。设计使用消息队列来维护设备系统与SHM的信息交换。尽管微软的消息队列(MSMQ)具有平台相关性,在其他平台可以自己设计类似功能。消息队列的优点就是能存储信息,检索信息。我们使用两个消息对列来连接设备系统和SHM,见下图。SHM(设备驱动程序)发给设备系统的消息称为“通知”(Notify),设备系统发给驱动程序的消息称为“消息”(Message)。
之所以使用两个队列,是不想花CPU时间来检索判断信息内容。设备系统使用线程只管从Notify队列取出“指令数据”来处理,而SHM只管从Message队列获取设备信息的状态数据即可。当然,SHM与SHS之间也维护着一对(不是一个)MSMQ。
由于大量使用MSMQ,程序使用线程的方式操作队列。否则UI界面会有“阻塞”显现,或者异步方式操作队列。如果设备系统使用TCP/IP与监控程序SHM通信,那么在驱动程序和设备系统程序中,都不需要操作队列即可。
为方便查看IPC的内容,SHM提供了查看共享内存和消息队列内容的UI,见下图。
进程间通信对象,封装在ShareMemory对象中,见下面的完整代码。共享内存使用互斥来同步数据访问。消息队列的访问,由windows的队列服务维护,无需开发者操心。
namespace HomeLibrary.ShareMemory
public class ShareMemory
#region 共享内存操作函数的外部声明
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, uint flProtect,
uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr OpenFileMapping(int dwDesiredAccess,
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, string lpName);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr MapViewOfFile(IntPtr hFileMapping, uint dwDesiredAccess,
uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool UnmapViewOfFile(IntPtr pvBaseAddress);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
[DllImport("kernel32", EntryPoint = "GetLastError")]
public static extern int GetLastError();
#endregion
#region 相关常数声明
const int ERROR_ALREADY_EXISTS = 183;
const int FILE_MAP_COPY = 0x0001;
const int FILE_MAP_WRITE = 0x0002;
const int FILE_MAP_READ = 0x0004;
const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004;
const int PAGE_READONLY = 0x02;
const int PAGE_READWRITE = 0x04;
const int PAGE_WRITECOPY = 0x08;
const int PAGE_EXECUTE = 0x10;
const int PAGE_EXECUTE_READ = 0x20;
const int PAGE_EXECUTE_READWRITE = 0x40;
const int SEC_COMMIT = 0x8000000;
const int SEC_IMAGE = 0x1000000;
const int SEC_NOCACHE = 0x10000000;
const int SEC_RESERVE = 0x4000000;
const int INVALID_HANDLE_VALUE = -1;
#endregion
#region 类属性声明
private IntPtr m_hSharedMemoryFile = IntPtr.Zero;
private long m_MemSize = 0;
private Mutex mutex; //用于进程间同步
public IntPtr m_pwData = IntPtr.Zero;
public bool m_ok = false; //共享内存是否可用
//public List<stringJson> Messages; //其他平台自己定义.....
//public List<stringJson> Notifies;
public MessageQueue MQNotify; //发送通知的队列
public MessageQueue MQMessage; //接收消息的队列
#endregion
public int MaxCount;
public int MAXITEMS
get return MaxCount;
set
if (value >= 10 && value <= 1000) //最多1000保存1000条消息
MaxCount = value;
public ShareMemory(Mutex mutex)
this.mutex = mutex;
//Messages = new List<stringJson>();
//Notifies = new List<stringJson>();
//MAXITEMS = 100;
~ShareMemory()
Close();
public string mqnotifyfn = "";
public string mqmessagefn = "";
public int Init(string strName, long longSize)
if (longSize <= 0 || longSize > 0x00800000) longSize = 0x00800000;
m_MemSize = longSize;
mqnotifyfn = ".\\\\Private$\\\\" + strName + "Notify";
if (!MessageQueue.Exists(mqnotifyfn))
MQNotify = MessageQueue.Create(mqnotifyfn);
MQNotify.SetPermissions("Administrators", MessageQueueAccessRights.FullControl);
else
//if MessageQueue.Exists(mqnotifyfn))
MQNotify = new MessageQueue(mqnotifyfn);
//MQNotify.Purge();
MQNotify.Label = mqnotifyfn;
MQNotify.Formatter = new System.Messaging.BinaryMessageFormatter();
mqmessagefn = ".\\\\Private$\\\\" + strName + "Message";
if (!MessageQueue.Exists(mqmessagefn))
MQMessage = MessageQueue.Create(mqmessagefn);
MQMessage.SetPermissions("Administrators", MessageQueueAccessRights.FullControl);
else
MQMessage = new MessageQueue(mqmessagefn);
//MQMessage.Purge();
MQMessage.Formatter = new System.Messaging.BinaryMessageFormatter();
MQMessage.Label = mqmessagefn;
if (strName.Length > 0) //创建共享内存(INVALID_HANDLE_VALUE)
m_hSharedMemoryFile = OpenFileMapping(FILE_MAP_WRITE | FILE_MAP_READ, false, strName);
if (m_hSharedMemoryFile == IntPtr.Zero) //没有指定的共享内存,创建它
m_hSharedMemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, IntPtr.Zero,
(uint)PAGE_READWRITE, 0, (uint)longSize, strName);
if (m_hSharedMemoryFile == IntPtr.Zero) return 1; //无法使用共享内存
//通过内存映射,获得一个指针:指向该内存块
m_pwData = MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_WRITE, 0, 0, (uint)longSize);
if (m_pwData == IntPtr.Zero) //创建内存映射失败
m_ok = false;
CloseHandle(m_hSharedMemoryFile); //关闭共享内存句柄,释放资源
return 2; //映射失败返回2
m_ok = true;
return 0; //创建成功
else //参数错误返回
return 4;
/// ﹤summary﹥
/// 关闭共享内存
/// ﹤/summary﹥
public void Close()
if (m_ok)
UnmapViewOfFile(m_pwData);
CloseHandle(m_hSharedMemoryFile);
//if (Messages != null) Messages.Clear();
//if (Notifies != null) Notifies.Clear();
public void DeleteNotifyMQ()
if (MessageQueue.Exists(mqnotifyfn))
MessageQueue.Delete(mqnotifyfn);
public void DeleteMessageMQ()
if (MessageQueue.Exists(mqmessagefn))
MessageQueue.Delete(mqmessagefn);
/// ﹤summary﹥
/// 写入数据到共享内存
/// ﹤/summary﹥
public void ObjectToShareMemory(object struncture)
if (m_ok)
mutex.WaitOne();
Marshal.StructureToPtr(struncture, m_pwData, true);
mutex.ReleaseMutex();
/// ﹤summary﹥
/// 共享内存数据转移到对象,方便处理,一般返回的对象强制转换为应用程序的某个类型
/// ﹤/summary﹥
public object ShareMemoryToObject(object struncture)
if (m_ok)
mutex.WaitOne();
object ob = Marshal.PtrToStructure(m_pwData, struncture.GetType());
mutex.ReleaseMutex();
return ob;
else return null;
public bool AddMessage(byte[] msg) //增加一个消息数据结构
if (!MessageQueue.Exists(mqmessagefn)) return false;
MQMessage = new MessageQueue(mqmessagefn);
Message Msg = new Message();
MQMessage.Formatter = new System.Messaging.BinaryMessageFormatter();
MQMessage.Formatter.Write(Msg, msg);
MQMessage.Send(Msg);
return true;
public bool AddMessage(stringJson msg) //增加一个消息数据结构
if (msg == null) return false;
return AddMessage(msg.GetBytes());
public stringJson GetMessage(int ms=20) //获取一个消息数据结构
if (!MessageQueue.Exists(mqmessagefn)) return null;
MQMessage = new MessageQueue(mqmessagefn);
MQMessage.Formatter = new System.Messaging.BinaryMessageFormatter();
try
System.Messaging.Message Msg = MQMessage.Receive(new TimeSpan(0, 0, 0, 0, ms));
if (Msg == null) return null;
return (stringJson.ConvertBytesTostringJson((byte[])Msg.Body, SmartHomeChannel.SHFLAG));
catch
return null;
public bool AddNotify(byte[] msg) //增加一个消息数据结构
if (!MessageQueue.Exists(mqnotifyfn)) return false;
MQNotify = new MessageQueue(mqnotifyfn); //重新连接,有可能断开了!!!
Message Msg = new Message();
MQNotify.Formatter = new System.Messaging.BinaryMessageFormatter();
MQNotify.Formatter.Write(Msg, msg);
MQNotify.Send(Msg);
return true;
public bool AddNotify(stringJson msg) //增加一个消息数据结构
if (msg == null) return false;
return AddNotify(msg.GetBytes());
public stringJson GetNotify(int ms=20) //获取一个消息数据结构
if (!MessageQueue.Exists(mqnotifyfn)) return null;
MQNotify = new MessageQueue(mqnotifyfn);
MQNotify.Formatter = new System.Messaging.BinaryMessageFormatter();
try
Message Msg = MQNotify.Receive(new TimeSpan(0, 0, 0, 0, ms));
if (Msg == null) return null;
return (stringJson.ConvertBytesTostringJson((byte[])Msg.Body, SmartHomeChannel.SHFLAG));
catch
return null;
public static bool CreateMutex(string mutexName, ref Mutex mutex) //创建互斥
bool doesNotExist = false;
bool unauthorized = false;
bool mutexWasCreated = false;
try // Attempt to open the named mutex.
mutex = Mutex.OpenExisting(mutexName);
catch (WaitHandleCannotBeOpenedException)
//Console.WriteLine("Mutex does not exist.");
doesNotExist = true;
catch (UnauthorizedAccessException)
//Console.WriteLine("Unauthorized access: 0", ex.Message);
unauthorized = true;
if (doesNotExist)
string user = Environment.UserDomainName + "\\\\" + Environment.UserName;
MutexSecurity mSec = new MutexSecurity();
MutexAccessRule rule = new MutexAccessRule(user,
MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Deny);
mSec.AddAccessRule(rule);
rule = new MutexAccessRule(user,
MutexRights.ReadPermissions | MutexRights.ChangePermissions, AccessControlType.Allow);
mSec.AddAccessRule(rule);
mutex = new Mutex(true, mutexName, out mutexWasCreated, mSec);
if (!mutexWasCreated)
//Console.WriteLine("Unable to Created the mutex.");
return false;
mutex.ReleaseMutex(); //新创建,释放?
else if (unauthorized)
try
mutex = Mutex.OpenExisting(mutexName,
MutexRights.ReadPermissions | MutexRights.ChangePermissions);
MutexSecurity mSec = mutex.GetAccessControl();
string user = Environment.UserDomainName + "\\\\" + Environment.UserName;
MutexAccessRule rule = new MutexAccessRule(user,
MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Deny);
mSec.RemoveAccessRule(rule);
rule = new MutexAccessRule(user,
MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow);
mSec.AddAccessRule(rule);
mutex.SetAccessControl(mSec);
//Console.WriteLine("Updated mutex security.");
mutex = Mutex.OpenExisting(mutexName);
catch (UnauthorizedAccessException)
//Console.WriteLine("Unable to change permissions: 0", ex.Message);
return false;
return true;
共享内存中存储的数据结构,定义在SmartHomeChannel对象中。主要定义了进程的一些基本信息。参见下面的完整代码。
namespace HomeLibrary.Type
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public class SmartHomeChannel //智能家居程序对象
public static int SHFLAG = 0x5A5A5A5A; //智能家居系统统一标识,便于各个厂家通信公用
public static string sharememoryfile = "SmartHomeShareFile";
public static string mutexname = "SmartHomeMutex";
public int appid; //某品牌智能家居唯一识别号
public bool canused; //系统能否被使用
public bool autostart; //自动运行
public bool loaded; //监控程序SHM是否已经加载,可用于防止启动多个进程
public bool Deviceloaded; //协调器或虚拟设备程序是否已经加载,可用于防止启动多个进程
public bool visible; //启动时是否显示窗口
public bool winvisible; //程序窗口目前是否显示
public int PID; //智能家居进程号,可以启动多个进程
public int DevicePID; //设备程序(协调器,虚拟设备程序)进程号
public int DeviceId; //设备号
public int Port; //TCP/IP通信端口号
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string password; //通行密码,所有指令必须带有密码
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string user; //TCP通信登录SHM的用户名带有密码
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string name; //系统名称:32Unicode字符
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string starttime; //系统启动时间
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string assembly; //实现了ISmartHome接口的程序集文件名
public override string ToString()
string result = "名称=" + name + ",识别号=" + appid.ToString() + ",\\r\\n";
result += "程序集=" + assembly + ",启动时间=" + starttime + ",\\r\\n";
result += "可用=" + canused.ToString() + ",自启动=" + autostart.ToString() + ",显示=" + visible.ToString() + ",\\r\\n";
result += "SHM PID=" + PID.ToString() + ",Device PID=" + DevicePID.ToString() +",\\r\\n";
result += "SHM加载否=" + loaded.ToString() + ",Device加载否=" +Deviceloaded.ToString() + "\\r\\n";
return result;
public stringJson TostringJson(Int32 flag)
stringJson json = new stringJson(flag);
json.AddNameValume("家居名称", name);
json.AddNameValume("家居PID", appid.ToString());
json.AddNameValume("程序集", assembly);
json.AddNameValume("启动时间", starttime);
json.AddNameValume("SHM PID", PID.ToString());
json.AddNameValume("Device PID", DevicePID.ToString());
json.AddNameValume("SHM 加载否", loaded.ToString());
json.AddNameValume("Device加载否", Deviceloaded.ToString());
return json;
有了这些基本的对象,似乎可以编写SHS和SHM程序了。不急,需要先写一个设备驱动程序,才好在SHS中使用。下节介绍如何开发符合SHP的设备驱动程序,并用例子来描述开发过程和结果。
以上是关于智能家居通用管理平台 - 进程间通信的主要内容,如果未能解决你的问题,请参考以下文章