返回首页

{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,跨应用程序边界的目标字符串消息发送和接收。库允许使用用户定义的伪"渠道"的消息,通过它可发送和接收。任何应用程序都可以发送邮件到任何通道,但它必须作为一个通道侦听注册,以便接收。这样,开发人员可以快速,编程设计如何最好的应用程序可以与对方和工作在和谐的沟通。例如:发送消息

// Send shutdown message a channel named commands

XDBroadcast.SendToChannel("commands", "shutdown");
例:听力频道{C}范例:处理消息。
// 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使用方法。
// 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);
进一步阅读{A7}历史2007年2月:初次发布二月,25日2007年:新增港口VB的演示2007年5月,29日:文章编辑和发布的主要CodeProject.com文章基地第二年6月,2008年:改进线程的支持,以避免应用程序挂

回答

评论会员:游客 时间:2011/12/07
感谢,实在是有帮助的:
乔恩史密斯0374
评论会员:游客 时间:2011/12/07
我读字节的数据通过MSMQ消息不断,并转换成结构,一段时间我的内存访问错误的HRESULT0X......我的程序停止。请帮我出如何可以处理的IntPtr,所以内存访问或损坏的内存错误可以解决....{BR}codeprespanclass="code-keyword"bool/spanbReceiveMessages=spanclass="code-keyword"true/span;spanclass="code-keyword"while/span(bReceiveMessages){ byte[]buffer=(byte[])message.Body;spanclass="code-comment"///spanspanclass="code-comment"getbytedatafrommessage(MSMQ)/spanTBCastMessageHeaderbcastHeader=spanclass="code-keyword"new/spanTBCastMessageHeader();spanclass="code-SDKkeyword"IntPtr/spanbcastHeaderPtr=GetstdHandle();bcastHeaderPtr=Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TBCastMessageHeader))); Marshal.Copy(buffer,spanclass="code-digit"0/span,bcastHeaderPtr,buffer.Length);bcastHeader=(TBCastMessageHeader)(Marshal.PtrToStructure(bcastHeaderPtr,typeof(TBCastMessageHeader)));spanclass="code-keyword"try/span{spanclass="code-comment"///spanspanclass="code-comment"dosomestuffherecontinusly/span}spanclass="code-keyword"catch/span(Exceptionecp){}spanclass="code-keyword"finally/span{Marshal.FreeHGlobal(bcastHeaderPtr);GC.Collect();}}/pre/code
TheCodeKing
评论会员:有趣的文章 时间:2011/12/07
avnkkishore
评论会员:游客 时间:2011/12/07
我想我会花一些时间,很多人似乎要寻找一个解决方案,从Windows服务内。正如我前面提到的Windows消息是不是真的适合这种情况下,作为WindowsMessaging需要消息泵和一个窗口,发送和接收消息。在保持原来的目标库提供进程间跨越AppDomain通信的零配置机制,我拿出文件IO流为基础的解决方案。我也看着几个替代品,如邮筒和NamedPipes,但他们都不符合要求。在以后的文章中。中我将解释我的想法现在有XDMessaging-2.0库的升级版本低于现在包括2种模式。WindowsMessaging和iostream的。只有iostream的支持Windows服务,也为窗体应用程序。WindowsMessaging仍然会为形式的应用程序之间的高性能通信的首选机制。codeprespanclass="code-comment"///spanspanclass="code-comment"TouseIOStream/spanIXDBroadcastbroadcast=XDBroadcast.CreateBroadcast(XDTransportMode.IOStream);IXDListenerlistener=XDListener.CreateListener(XDTransportMode.IOStream);listener.MessageReceived+=XDMessageHandler(spanclass="code-keyword"this/span.listener_MessageReceived);... spanclass="code-comment"///spanspanclass="code-comment"TouseWindowsMessaging/spanIXDBroadcastbroadcast=XDBroadcast.CreateBroadcast(XDTransportMode.WindowsMessaging);IXDListenerlistener=XDListener.CreateListener(XDTransportMode.WindowsMessaging);listener.MessageReceived+=XDMessageHandler(spanclass="code-keyword"this/span.listener_MessageReceived); .../pre/code{A8}麦克{A9}
TheCodeKing
评论会员:游客 时间:2011/12/07
您好,我有两个应用,一个是Windows服务广播的消息,其中第二个Windows窗体应用程序接收,并做一些处理。从Windows服务的广播消息不收到Windows窗体应用程序imgsrc=http://www.orcode.com/upimg/2011_12_07_12_08_31_1.gif当我测试了一个控制台应用程序相同的情况下,我Winfroms应用程序工作正常。只是想知道有什么我失踪Windows服务运行。我的工作在。NETFramework3.5在WindowsXP环境。预先感谢。的问候,阿迪
avnkkishore
评论会员:游客 时间:2011/12/07
您将需要设置"允许服务与桌面交互"设置Windows服务,否则服务将无法发送或接收Windows消息。{A9}
TheCodeKing
评论会员:游客 时间:2011/12/07
感谢您的答复。我没有做下面的代码在"OnCommitted"我ProjectInstaller事件的互动服务。//这里是我们的价值在注册表中的位。//我们的服务抓斗的子项的RegistryKeyckey=Registry.LocalMachine.OpenSubKey(-;@"SYSTEM\CurrentControlSet\服务\"this.serviceInstaller1.ServiceName,TRUE);//好,总是做错误检查!(ckey!=NULL){-;//OK现在,让我们确保"类型"的值是存在的,-;//然后就可以做我们的位运算。-;(!ckey.GetValue("类型")=NULL)-;{-;ckey.SetValue("类型",((INT)ckey.GetValue("类型")|256))-;}}我观察到,服务的注册表值与上面的代码的变化。但仍然是我的Winform的不接收广播messagesnbsp;imgsrc=http://www.orcode.com/upimg/2011_12_07_12_08_31_1.gif任何帮助表示高度赞赏。,阿迪
avnkkishore
评论会员:游客 时间:2011/12/07
你看到了什么,如果你右击你的服务在服务控制台和导航"登录"标签的?"允许服务与桌面交互"应进行检查。如果是,仍然无法正常工作采取进一步下降WytekSzymanski题为"一个伟大的构想,但..在看看评论"。他请张贴得到消息,在服务工作的一个代码修复,虽然我没有机会来看看这个自己或者尝试一下。{A9}
TheCodeKing
评论会员:游客 时间:2011/12/07
我看到的"允许服务与桌面"服务属性检查交互。我试图修改为在给定的XDListener的"一个伟大的构想,但是......"线程,但其预期的Win32类味精财产,GetMessage函数和DispatchMessage方法,我无法找到。在这一点上,我得到了深刻的印象。更新XDListener的手段,使得Windows窗体应用程序,从Windows服务发送的BoradCast消息听。请让我知道我的理解是否正确。,阿迪
avnkkishore
评论会员:游客 时间:2011/12/07
你需要这些方法添加到Win32。当我得到一些时间,我会尝试自己。希望有帮助。codeprespanclass="code-keyword"public/spanspanclass="code-keyword"const/spanuintWM_QUIT=0x0012; [DllImport(spanclass="code-string""/spanspanclass="code-string"user32"/span)][spanclass="code-keyword"return/span:MarshalAs(UnmanagedType.Bool)]spanclass="code-keyword"public/spanspanclass="code-keyword"static/spanspanclass="code-keyword"extern/spanspanclass="code-keyword"bool/spanGetMessage(outMSGlpMsg,spanclass="code-SDKkeyword"IntPtr/spanhWnd,uintwMsgFilterMin,uintwMsgFilterMax); [spanclass="code-keyword"return/span:MarshalAs(UnmanagedType.Bool)][DllImport(spanclass="code-string""/spanspanclass="code-string"user32"/span,CharSet=CharSet.Auto)]spanclass="code-keyword"public/spanspanclass="code-keyword"static/spanspanclass="code-keyword"extern/spanspanclass="code-keyword"bool/spanPostThreadMessage(uintthreadId,uintmsg,spanclass="code-SDKkeyword"UIntPtr/spanwParam,spanclass="code-SDKkeyword"IntPtr/spanlParam); [DllImport(spanclass="code-string""/spanspanclass="code-string"user32"/span,CharSet=CharSet.Auto)]spanclass="code-keyword"public/spanspanclass="code-keyword"static/spanspanclass="code-keyword"extern/spanspanclass="code-SDKkeyword"IntPtr/spanDispatchMessage([In]spanclass="code-keyword"ref/spanMSGlpmsg); [StructLayout(LayoutKind.Sequential)]spanclass="code-keyword"public/spanspanclass="code-keyword"struct/spanMSG{spanclass="code-keyword"public/spanspanclass="code-SDKkeyword"IntPtr/spanhwnd;spanclass="code-keyword"public/spanspanclass="code-SDKkeyword"UInt32/spanmessage;spanclass="code-keyword"public/spanspanclass="code-SDKkeyword"IntPtr/spanwParam;spanclass="code-keyword"public/spanspanclass="code-SDKkeyword"IntPtr/spanlParam;spanclass="code-keyword"public/spanspanclass="code-SDKkeyword"UInt32/spantime;spanclass="code-keyword"public/spanPOINTpt;} [StructLayout(LayoutKind.Sequential)]spanclass="code-keyword"public/spanspanclass="code-keyword"struct/spanPOINT{spanclass="code-keyword"int/spanx;spanclass="code-keyword"int/spany;}/pre/code{A9}
TheCodeKing
评论会员:游客 时间:2011/12/07
。很快,我将描述我做了什么我已经更新代码,XDListener类(每个线程)的Win32类和我的Windows窗体应用程序中引用。以下是代码,我已经写在我的Windows窗体的Form_Load()方法codeprelistener=spanclass="code-keyword"new/spanXDListener();listener.MessageReceived+=spanclass="code-keyword"new/spanXDListener.XDMessageHandler(listener_MessageReceived);listener.RegisterChannel(spanclass="code-string""/spanspanclass="code-string"Status"/span);listener.Start();/pre/code我的Windows服务包含XDMessaging图书馆,因为我不使用XDListener类非修改后的版本。以下是我在OnStart()和OnStop()我的服务活动,一行代码。codepreXDBroadcast.SendToChannel(spanclass="code-string""/spanspanclass="code-string"Status"/span,spanclass="code-string""/spanspanclass="code-string"ServiceStarted"/span);/pre/code我做正确或任何更改都需要。,阿迪
TheCodeKing
评论会员:游客 时间:2011/12/07
问题是,发送邮件服务需要获得一个句柄到桌面和枚举所有寻找已注册接收数据的窗口的子窗口。首先,服务是无法获得一个句柄到用户桌面,除非"允许服务与桌面交互"设置。其次,它看起来像运行时作为一个服务的过程中被限制的Windows,它可以访问,因此不枚举所有的孩子都。这就是为什么不发送消息,因为它不能看到客户端窗口。该库是不是真的在一个窗口服务中使用而设计,因为它是内置在Windows消息需要一个窗口和一个消息泵功能。你会过得更好使用插座一样,{A13}为您服务的跨进程通信技术。我建议在WCF中,看看如果你能找到适合您需要的东西。{A9}
bilo81!真棒,你只是做了我的一天
{S3}
评论会员:TheCodeKing 时间:2011/12/07
我发布一个新XDMessaging的版本现在支持额外的模式,从Windows服务内。看到后,"XDMessaging 2.0现在支持Windows服务"在评论部分。
{A9}
评论会员:星乔浮游生物 时间:2011/12/07
我真的很喜欢的文章,5星为我
另一种方法可能在不知情的情况下,当然使用的插座(可能使用数据报套接字),并创建一个库,以便客户端使用套接字透明,这是一个真正的PInvoke和使用非托管代码在C#虽然良好的洞察力,和我很欣赏你的做法。保持良好的工作
评论会员:TheCodeKing 时间:2011/12/07
感谢您的关注

插座和甚至NamedPipes的问题是,他们需要一个客户端和服务器的方法。首先必须创建服务器之前,客户端可以连接。实际上,每个人都有对其他事先知道。这个库背后的想法是,任何进程都可以在一个更断开的时尚广播消息的任何侦听器。随着XDMessaging库是不需要服务器和客户端,正因为如此,这可以是一个快速和便捷的信息平台的优势。
{A9}
评论会员:kurtsune 时间:2011/12/07
这是一个非常有用的实用工具 - 感谢! {S4}的

我认为这将是真正用于监测/分析Web服务方便,但我碰钉子。虽然VS开发的网络服务器上运行的Web服务时,它工作得很好,它不会在本地IIS时,它的运行。必须有另一个具体的IIS进程,阻止它的边界问题。不幸的是,我没有此刻时间调查,所以我会恢复到旧写入到一个文件的方法!

如果有办法得到这个工作在IIS,这将是完美的!

(作为一个简单的测试,在VS中创建一个新的Web服务,并把类似:

TheCodeKing.Net.Messaging.XDBroadcast.SendToChannel("状态","Hello World"的放大器; Now.Ticks)

在HelloWorld示例方法)
评论会员:会员6038196 时间:2011/12/07
2.0版现在在IIS作品,看到在评论中包含的新版本。
{A9}
评论会员:TheCodeKing 时间:2011/12/07
此代码在C#工程:
        protected override void WndProc(ref Message msg)

        {

            base.WndProc(ref msg);

            if (msg.Msg == Win32.WM_COPYDATA)

            {

                if (MessageReceived == null) return;

                DataGram dataGram = DataGram.FromPointer(msg.LParam);

                MessageReceived(this, new XDMessageEventArgs(dataGram));

            }

        }

此代码不会在VB.NET:

       Protected Overloads Overrides Sub WndProc(ByRef msg As Message)

            MyBase.WndProc(msg)

            If msg.Msg = im.Win32.WM_COPYDATA Then

                If MessageReceived Is Nothing Then  'ERROR see below

                    Return

                End If

                Dim dataGram__1 As Sune2.Messaging.DataGram = Sune2.Messaging.DataGram.FromPointer(msg.LParam)

                RaiseEvent MessageReceived(Me, New MessageEventArgs(dataGram__1))

            End If

        End Sub
错误:'MessageReceived公共事件(发送者为对象,作为MessageEventArgs发送)"是一个事件,并不能直接调用。

我会很感激,如果你能解释如何在VB。

/ K
评论会员:cwfong 时间:2011/12/07
最后一个环节,"界限:进程和应用程序域"被打破,它应该指向:http://msdn.microsoft.com/en-us/library/kt21t9h7(与80)ASPX
评论会员:。TheCodeKing 时间:2011/12/07
感谢,我所要求的链接更新{五}
A9 {}
评论会员:Suneel_Sarode 时间:2011/12/07
。一个简单的方法是AppDomain的CreateInstanceFromAndUnwrap方法,以创建代理到另一个应用程序域,您可以控制​​的应用程序域,
评论会员:TheCodeKing 时间:2011/12/07
不幸的是这并没有与进程间通信的帮助。
{A9}