{A}{S0}简介
本文演示了一个跨AppDomain的通信的快速和易于使用的执行。NET通过利用Windows的本地消息。 XDMessaging库是根据最近的Vista的项目,需要在锁定环境很多跨AppDomain的沟通,以帮助快速发展而开发的代码。它被证明是非常有用的情景。NET Remoting的是不切实际的,如果不是不可能的,实际上解决了更多比我想象由于其简单的问题。该图书馆的目的是在同一箱的情况下,多个应用程序之间发送消息。例如,一个任务托盘应用程序可能需要沟通或监视一个单独的桌面应用程序。库不通过网络实现跨域通信,这种情况下,NET Remoting是足够的。
更新:XDMessaging 2.0现已,并介绍了Windows服务和控制台应用程序的支持。背景
那么,为什么不使用。NET Remoting?那么,在过去,我个人觉得这种极其繁琐的设置和配置。另一个问题是缺乏有用的错误报告出问题时,无不与权限。不要误会我,我不反对。NET Remoting的。它已经超过了很多我自己的实现的功能,当然不局限于通信上的一个盒子。然而,同框通信,它并不需要复杂。那么,为什么不利用Windows消息?毕竟,这是正是这样做的目的使用非托管应用程序的机制。现在好了,有一个想法??/ P>
如果你从来没有听说过他们,Windows消息是由Windows操作系统使用的低级通信,广播信息,用户输入的,系统的变化和其他事件,在系统上运行的应用程序可以反应。例如,应用程序重绘触发WM_PAINT消息。以及系统消息,也可能非托管应用程序定义自定义的Windows消息,并使用他们的沟通与其他Windows。这些通常采取的形式WM_USER消息。如果你有安装间谍(Visual Studio工具),您可以监视一个窗口中实时接收的所有消息。XDMessaging图书馆
XDMessaging库提供了一个易于使用的,零配置到同一盒跨AppDomain的通信解决方案。它提供了一个简单的API,跨应用程序边界的目标字符串消息发送和接收。库允许使用用户定义的伪"渠道"的消息,通过它可发送和接收。任何应用程序都可以发送邮件到任何通道,但它必须作为一个通道侦听注册,以便接收。这样,开发人员可以快速,编程设计如何最好的应用程序可以与对方和工作在和谐的沟通。例如:发送消息
例:听力频道{C}范例:处理消息。// Send shutdown message a channel named commands
XDBroadcast.SendToChannel("commands", "shutdown");
信使演示// Attach an event handler to our instance
listener.MessageReceived+=XDMessageHandler(this.listener_MessageReceived);
// process the message
private void listener_MessageReceived(object sender, XDMessageEventArgs e)
{
// e.DataGram.Message is the message
// e.DataGram.Channel is the channel name
switch(e.DataGram.Message)
{
case "shutdown":
this.Close();
break;
}
}
要查看演示,您将需要启动的Messenger.exe应用程序的多个实例。演示应用程序服务没有任何实际用途以外的其他证明XDMessaging库的使用。它显示了如何将邮件可能会被传递给一个跨应用程序边界的桌面应用程序的多个实例。应用程序使用任意两个通道,命名状态和UserMessage。窗口,如onClosing和onload事件广播状态的通道(绿色显示)和用户信息的广播消息UserMessage通道(显示为蓝色)。通过选中或取消选中的选项,你可以切换窗口将监听通道的消息。它如何工作
库使得使用Windows系统的类型WM_COPYDATA消息。此系统消息允许携带一个指针,我们要复制的数据,在这种情况下,字符串数据被多个应用程序之间传递。这是发送到其他窗口SendMessage函数的Win32 API使用的PInvoke。[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
[DllImport("user32", CharSet = CharSet.Auto)]
public extern static int SendMessage(IntPtr hwnd, int wMsg,
int wParam, ref COPYDATASTRUCT lParam);
COPYDATASTRUCT结构包含有关消息,我们要转移到另一个应用程序的数据信息。这是引用的成员lpData和dwData。 lpData是一个字符串,它是存储在内存中的数据的指针。 dwData是传输数据的大小。在我们的例子中不使用cdData成员。为了传递数据,我们必须首先分配的消息字符串在内存中的地址,并获得一个指向这个数据。要做到这一点,我们使用编组API如下:// Serialize our raw string data into a binary stream
BinaryFormatter b = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
b.Serialize(stream, raw);
stream.Flush();
int dataSize = (int)stream.Length;
// Create byte array and transfer the stream data
byte[] bytes = new byte[dataSize];
stream.Seek(0, SeekOrigin.Begin);
stream.Read(bytes, 0, dataSize);
stream.Close();
// Allocate a memory address for our byte array
IntPtr ptrData = Marshal.AllocCoTaskMem(dataSize);
// Copy the byte data into this memory address
Marshal.Copy(bytes, 0, ptrData, dataSize);
字符串数据,现在我们在上面的代码ptrData指针引用,在内存中,我们可以创建我们COPYDATASTRUCT实例,并填充相应的lpData和dwData成员。所以,现在我们已经包装成一个COPYDATASTRUCT对象的消息,我们已经准备好发送到另一个窗口,使用SendMessage API。但是,要做到这一点,我们首先需要知道哪些应用程序应该接收数据,即这是听取和正确的通道上。此外,如果应用程序没有窗口句柄发送消息?
为了克服这一点,我们使用一些本地的窗口属性和我们XDListener类。当调用一个类的实例是,它会创建一个隐藏的窗口在桌面上的所有Windows消息监听器行为。这是通过扩展NativeWindow类的System.Windows.Forms的。通过覆盖WndProc方法,这可以让我们筛选的Windows消息,并期待我们WM_COPYDATA消息包含消息数据。
XDListener类也使得窗口的属性创建属性标志,表明实例正在侦听的渠道,因此它应得到哪些消息。广播消息时,它列举了所有的桌面使用EnumChildWindows的Win32 API的Windows。它看起来窗口上表示频道的名称为一个标志(属性名称)。如果找到了,在Windows WM_COPYDATA消息发送到该窗口。一旦出现,它会被捕获和XDListener实例拥有隐藏窗口处理。要读取消息数据,我们使用本地Windows消息的lParam为了扩大COPYDATASTRUCT实例。由此,我们可以找到并恢复原来这是存储在内存中的字符串消息。// NativeWindow override to filter our WM_COPYDATA packet
protected override void WndProc(ref Message msg)
{
// We must process all the system messages and propagate them
base.WndProc(ref msg);
// If our message
if (msg.Msg == Win32.WM_COPYDATA)
{
// msg.LParam contains a pointer to the COPYDATASTRUCT struct
Win32.COPYDATASTRUCT dataStruct =
(Win32.COPYDATASTRUCT)Marshal.PtrToStructure(
msg.LParam , typeof(Win32.COPYDATASTRUCT));
// Create a byte array to hold the data
byte[] bytes = new byte[this.dataStruct.cbData];
// Make a copy of the original data referenced by
// the COPYDATASTRUCT struct
Marshal.Copy(this.dataStruct.lpData, bytes, 0,
this.dataStruct.cbData);
// Deserialize the data back into a string
MemoryStream stream = new MemoryStream(bytes);
BinaryFormatter b = new BinaryFormatter();
// This is the message sent from the other application
string rawmessage = (string)b.Deserialize(stream);
// do something with our message
}
}
注意,因为消息存储在内存中,而它的广播到其他应用程序,我们必须记住释放的消息后,内存已经寄出。接收数据的每个窗口,将它自己的拷贝,因此,原始数据可以被摧毁的消息已发送尽快安全。这是必要的,因为数据是存储在非托管内存,否则会导致内存泄漏。// Free the memory referenced by the given pointer
Marshal.FreeCoTaskMem(lpData);
下面是其他的PInvoke用于设置和取消窗口的属性,以及枚举桌面Windows使用方法。
进一步阅读{A7}历史2007年2月:初次发布二月,25日2007年:新增港口VB的演示2007年5月,29日:文章编辑和发布的主要CodeProject.com文章基地第二年6月,2008年:改进线程的支持,以避免应用程序挂// Delegate used during window enumeration
public delegate int EnumWindowsProc(IntPtr hwnd, int lParam);
// The Win32 API used to enumerate children of the desktop window
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr hwndParent,
EnumWindowsProc lpEnumFunc, IntPtr lParam);
// The API used to look for a named property on a window
[DllImport("user32", CharSet = CharSet.Auto)]
public extern static int GetProp(IntPtr hwnd, string lpString);
// The API used to set a named property on a window, and
// hence register a messaging channel
[DllImport("user32", CharSet = CharSet.Auto)]
public extern static int SetProp(IntPtr hwnd, string lpString, int hData);
// The API used to remove a property from a window, and hence unregister a
// messaging channel
[DllImport("user32", CharSet = CharSet.Auto)]
public extern static int RemoveProp(IntPtr hwnd, string lpString);