Introduction
The XDMessaging library provides an easy-to-use, zero configuration solution to inter-process communication for .NET applications. It provides a simple API for broadcasting and receiving messages across application domain, process, and even network boundaries.
The library allows the use of user-defined pseudo channels through which messages may be sent and received. Any application can send a message to any channel, but it must register as a listener with the channel in order to receive. In this way developers can quickly and programmatically devise how their applications will communicate with each other best to work in harmony.
The messages may optionally be propagated to other processes over a network automatically.
Installation
Install the library using Nuget.
PM> Install-Package XDMessaging
Advantages
The XDMessaging library offers some advantages over other IPC technologies like WCF, .Net Remoting, Sockets, NamedPipes and MailSlots. To begin with the library does not require a server-client relationship as there is no physical connection between processes.
With XDMessaging messages can be broadcast by multiple applications and instantly received by multiple listeners in a disconnected fashion. It's also worth noting that most of the existing IPC implementations require the opening of specific ports and somewhat painful configuration of settings to make work. With XDMessaging there is no configuration, the API determines where messages are sent, and which messages are received using pseudo channels.
Network Propagation
Network propagation is a feature of the library that allows messages to leverage HighPerformanceUI or Compatibility modes, whilst additionally distributing messages to a remote server. RemoteNetwork mode is used under the hood to transfer messages to disconnected servers, and messages are rebroadcast by a slave IXDListener instance using the original transport mode. If the slave instance terminates for any reason, then another listener instance will automatically take it’s place.
Using the Library
To use the library, create an instance of a IXDBroadcaster and use this to send a message to a named channel. You can then create an instance of IXDListener to receive messages on a particular channel. The channels are arbitrary strings chosen to represent a channel, and are not case sensitive.
Before creating the broadcaster and listener instances you must first decide which transport mode you want to use for the library. There are 3 modes as follows and each has advantages over the other. Modes are abstracted from their specific implementations for extensibility, and loosely coupled. In order to use one of the modes the appropriate assembly must be referenced in the project in addition to XDMessaging.
Transport Modes
- Compatibility: The default implementation uses file based IO to broadcast messages via a shared directory. A
FileSystemWatcheris used within listener classes to monitor changes and trigger theMessageReceivedevent containing the broadcast message. This mode can be used in Windows Services, console applications, and Windows Forms based applications. Channels are created as separate directories on the file system for each channel. The temporary directories should be accessible by all processes, and there should be no need for manual configuration. To use this implementation add a reference toXDMessaging.Transport.IOStreamin your project. - HighPerformanceUI: The default implementation uses the
WM_COPYDATAWindows Message to copy data between applications. The broadcaster implementation sends the Windows Messages directly to a hidden window on the listener instance, which dispatches theMessageReceivedevent with the copied data. Channels are created by adding/removing Windows properties. This offers the most performant solution for Windows Forms based applications, but does not work for Windows Services, Console apps, or other applications without a message pump. To use this implementation add a reference toXDMessaging.Transport.WindowsMessagingin your project. - RemoteNetwork: By default this uses
Amazon Web Servicesto implement a subscriber/publisher implementation for broadcasting messages over a network and interprocess. There may be associated costs involved in using this mode, and you will need to supply valid Amazon account credentials. This mode is used internally to send messages from other transport modes over the network when using network propagation mode. To use this implementation add a reference toXDMessaging.Transport.Amazonin your project.
Note: Messages broadcast using a particular mode can only be read by applications using a listener in the same mode. For example, Compatibility listeners cannot read messages broadcast in the HighPerformanceUI mode. It is possible to broadcast in several modes at the same time, see documentation for further details.
See the included sample applications for more information on using the library in Windows Forms applications or within a Windows Service.
Messaging Demo
Download the source code and demo application from GitHub. Refer to user guide for more information.
39 comments:
Hi. Do you plan to upload to NuGet the library compiled for .Net Framework 4.0?
Hi the version that's there works with .Net Framework 4.0. Are you having a specific issue?
The messages not sending from Windows Service:
protected override void OnStart(string[] args)
{
IXDBroadcast ixdbBroadcast = XDBroadcast.CreateBroadcast(XDTransportMode.WindowsMessaging);
try
{
IPAddress localIPAdress = IPAddress.Parse(clsIni.Read_ini_settings("[SETTINGS]", "WSIP"));
int localPort = Convert.ToInt32(clsIni.Read_ini_settings("[SETTINGS]", "WSPort"));
asyncServer = new clsSocket.AsyncServer(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
asyncServer.Start(localIPAdress, localPort);
ixdbBroadcast.SendToChannel("gposWS", "Start");
clsSystemEvents.writeEvent("gpos.ge windows სერვისი ჩაიტვირთა1.", EventLogEntryType.Information, 1);
}
catch (Exception exc)
{
clsSystemEvents.writeEvent(exc.Message, EventLogEntryType.Error, 0);
}
}
WIndowsMessaging doesn't work from a Windows service as it can't access the desktop windows to send messages, use IOStream mode instead.
Thank you for quick answer. I changed it and am able to communicate, but now I have another problem. When message arrives program calls IXDListener_MessageReceived event. in this event I try to change lblVersion text, but it gives error:
Cross-thread operation not valid: Control 'lblVersion' accessed from a thread other than the thread it was created on.
This occurs when trying to update the UI from a non-UI thread. The solution is to use BeginInvoke.
public void ChangeUITitle(string title)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new Invoker(ChangeUITitle), title);
return;
}
this.Text = title;
}
Thanks, it was useful for me.
I had one more problem:
Don't now how to create serializable object, for sending via IOStream, from the class with properties:
private string ConnIdent;
private DateTime ConnDateTime;
private string ConnIP;
private int ConnPort;
Could you provide an example?
Hi all you need to do is add the [Serializable] attribute above the class decloration.
[Serializable]
public class MyClass
{
...
}
I created class in the Windows Service and Windows form program:
[Serializable]
public class clsConnectionLogs
{
private string pConnIdent;
private DateTime pConnDateTime;
private string pConnIP;
private int pConnPort;
public string ConnIdent {
get { return pConnIdent; }
set { pConnIdent = value.Trim(); } }
public DateTime ConnDateTime {
get { return pConnDateTime; }
set { pConnDateTime = value; } }
public string ConnIP {
get { return pConnIP; }
set { pConnIP = value.Trim(); } }
public int ConnPort {
get { return pConnPort; }
set { pConnPort = value; } }
}
From windows service I sending like this:
IXDBroadcast ixdbBroadcast = XDBroadcast.CreateBroadcast(XDTransportMode.IOStream);
clsConnectionLogs objClsConnectionLogs = new clsConnectionLogs();
objClsConnectionLogs.ConnIdent = intClientCount.ToString();
objClsConnectionLogs.ConnDateTime = System.DateTime.Now;
objClsConnectionLogs.ConnIP = ((IPEndPoint)workerSocket.RemoteEndPoint).Address.ToString();
objClsConnectionLogs.ConnPort = ((IPEndPoint)workerSocket.RemoteEndPoint).Port;
ixdbBroadcast.SendToChannel("gposWS", objClsConnectionLogs);
in Windows Form application in IXDListener_MessageReceived event i wrote:
TypedDataGram typedMsg = e.DataGram;
if (typedMsg.IsValid == true)
customData = typedMsg.Message;
BUT every time i'm getting typedMsg.IsValid=false and typedMsg.Message=null
e.DataGram looks like:
{gposWS:AAEAAAD/////AQAAAAAAAAAMAgAAAEV3d3cuZ3Bvcy5nZV93cywgVmVyc2lvbj0yLjAuMi4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPW51bGwFAQAAACB3d3cuZ3Bvcy5nZV93cy5jbHNDb25uZWN0aW9uTG9ncwQAAAAKcENvbm5JZGVudA1wQ29ubkRhdGVUaW1lB3BDb25uSVAJcENvbm5Qb3J0AQABAA0IAgAAAAYDAAAAATGGeXKSbH7PiAYEAAAACTEyNy4wLjAuMaK4AAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==}
How can I declare seriazable class and sens via IOStream???
Hi, sorry for taking so long to get back to you. It looks like it can't deserialize the data into a type. Are you referencing the same version of clsConnectionLogs in both projects? They will need to be the same version.
yes, the version ares same on both side. I solved this problem with sending xml type as a message. Then on other side I can read this xml and manage data. Anyway, Thank you.
It's simply amazing how easy it is t implement this library and how it just works - Well Done!
II'm experiencing one minor problem that I'm hoping you can assist me with. I'm using the IO Stream mode and the subscribed message received event is firing twice for every message received.
I tried the test messenger application that was included in the download and I experienced the same behavior. It works as expected when the mode is Win Msg, but IO Stream consistently results in duplicate messages.
I also upgraded to the latest version from NuGet and still received the same results.
Can you help point me in the right direction regarding what I may be doing wrong?
Thanks!
CodeKing - first let me thank you for a well designed and useful lib. Second - I have a problem. I put a Listener.Dispose() call in my form_closing() event and I get an exception when my form closes...
System.Runtime.InteropServices.SEHException was unhandled
HResult=-2147467259
Message=External component has thrown an exception.
Source=mscorlib
ErrorCode=-2147467259
StackTrace:
Server stack trace:
at TheCodeKing.Net.Messaging.Concrete.MailSlot.Native.CloseHandle(IntPtr handle)
at TheCodeKing.Net.Messaging.Concrete.MailSlot.MailSlotReader.Listen(Action`1 callback)
at TheCodeKing.Net.Messaging.Concrete.MailSlot.MailSlotWatcher.b__0()
at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Object[]& outArgs)
at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper(Message reqMsg, Boolean bProxyCase)
at System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(Object NotUsed, MessageData& msgData)
at System.Action.EndInvoke(IAsyncResult result)
at System.Runtime.Remoting.Messaging.AsyncResult.SyncProcessMessage(IMessage msg)
at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink)
at System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.ThreadPoolCallBack(Object o)
at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
InnerException:
Hi I've not seen that behaviour, what OS are you using? What version of .Net? Are you using Network propagation?
My OS is Win7 x64. .NET is 4.0
I am not specifically setting up any network propagation as I do not need this. Is there something I should do to DISABLE it (is it enabled by default)?
If I remove the Dispose() from my formclosing event and just let .NET cleanup my form using it's built in GC -- it all works fine.
So for right now - this is not holding me back because the life of the listener is the same as the life of the form for this app.
But if I ever need to use it in a situation where I need to Dispose() the listener explicitly this will become an issue.
The way it works is that every listener opts in by default to participate in network propagation. This is because of a limitation of MailSlots and to ensure all processes receive incoming network messages if they share the same mode.
If you don't care about not receiving messages from other boxes then you can opt out by passing true as an additional parameter when creating the listener. This will resolve your issue. It is correct to always call Dispose, and this looks like a bug.
The next version is a rewrite and removes MailSlots altogether in favour of AWS queues which has many advantages. Take a look at the develop branch on github. It's fully functional, I just need to fix an outstanding issue with queue clean up before I release it.
Impossibile avviare il servizio. System.BadImageFormatException: Impossibile caricare il file o l'assembly 'aaclient.dll' o una delle relative dipendenze. Il modulo doveva contenere un manifesto di assembly.
Nome file: 'aaclient.dll' ---> System.BadImageFormatException: Impossibile caricare il file o l'assembly 'aaclient.dll' o una delle relative dipendenze. Il modulo doveva contenere un manifesto di assembly.
Nome file: 'aaclient.dll'
in System.Reflection.AssemblyName.nGetFileInformation(String s)
in System.Reflection.AssemblyName.GetAssemblyName(String assemblyFile)
in XDMessaging.IoC.SimpleIocScanner.<>c__DisplayClass7.b__5(Assembly x)
in System.Linq.Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate)
in XDMessaging.IoC.SimpleIocScanner.b__4(String a)
in System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
in System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
in System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
...
Protected Overrides Sub OnStart(ByVal args() As String)
client = New XDMessaging.XDMessagingClient() <-- this make error
End Sub
Hi, can you email me some sample code to replicate the error. Are you using version 4.0.1 from NuGet?
Could you try version 4.0.2, I think that should resolve your issue.
I tried with vs 4.0.2 (nuget), but problem remain.
This is the start routine of a service.
I must prepare the source, i must cut much code.
The service start as localsystem
I not see sense send the code, the service is all on the OnStart() of the service, i need initialize the communication for receive/transmit. my configuration: win7 32bit, vs 2012 ultimate, framework 4
problem solved, there were some dll (old) in system32 that some of my libraries were referring, removed them, and now 'okay, but not understand what had nothing to do with the dll libraries xdmessaging.
Great I'm glad you got it working. The library implements a late binding IoC container which scans for assemblies in the same directory looking for transport implementations. I think it was falling over because it was finding a non-dotnet assembly. Soon as I get a chance I'll implement a fix.
another little thing, the New XDMessaging.XDMessagingClient() is much slower then the previous version. I see it when i start the service, before when i start immediatly the service is ready, now i must wait some seconds.
if XDMessagingClient fail, what is the name of the exception? (loaderexception?)
Please update the library, when i install my program i can't execute the service becouse start scan other assemblies and give error. :(
Hi thanks for your feedback, could you email me from now on at mike.carlisle@thecodeking.com and we can discuss any issues.
Hi,
I run my app from a readonly server share and when executing this line:
Dim client As New XDMessagingClient
... I get this error:
System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at:
System.IO.FileSystemEnumerableIterator`1.CommonInit() at:
System..IO.FileSystemEnumerableIterator`1..ctor(String path, String originalUserPath, String searchPattern, SearchOption searchOption, SearchResultHandler`1 resultHandler, Boolean checkHost) at:
System.IO.Directory.GetFiles(String path, String searchPattern, SearchOption searchOption) at:
XDMessaging.IoC.SimpleIocScanner.ScanAssemblies(String location, String searchPattern) i d:\Git\XDMessaging\XDMessaging.Net\Source\TheCodeKing.Utils\IoC\SimpleIoCScanner.cs:line 82 at:
XDMessaging.IoC..SimpleIocScanner.ScanAllAssemblies(String searchPattern) i d:\Git\XDMessaging\XDMessaging.Net\Source\TheCodeKing.Utils\IoC\SimpleIoCScanner.cs:line 57 at:
XDMessaging.IoC.SimpleIocContainerBootstrapper.Configure(IocContainer container) i :line 0 at:
TheCodeKing.Utils.IoC.SimpleIocContainer.Initialize(Action`1 configure) i d:\Git\XDMessaging\XDMessaging.Net\Source\TheCodeKing.Utils\IoC\SimpleIoCContainer.cs:line 78 at:
XDMessaging.IoC.SimpleIocContainerBootstrapper.<.cctor>b__9() i :line 0 at:
System.Lazy`1.CreateValue() at:
System.Lazy`1.LazyInitValue() at:
System.Lazy`1.get_Value() at:
XDMessaging.XDMessagingClient..ctor() i :line 0 at:
The exact error is:
Part of the path '\\myServer\myApps\myServer\myApps\' was not found.
Funny thing is, that the path is not correct, the correct path to the app is:
'\\myServer\myApps\'
So XDMessaging is combining the path wrong.
Any idea what is wrong?
Thanks
Hi please email me, this an issue with permissions. I will try and reproduce and implement a fix. The path is strange, no the library does not create this path itself.
Hi there,
Can this work with .Net 2 ?
Do you know any alternative if not?
Thanks
Ricardo
You can use version 3.0 with .Net 2 (Install-Package XDMessaging -Version 3.0). This has a slightly different API and does not support remote networking.
There are no fundamental reasons version 4.0 couldn't work with .Net 2.0 either, it's just I've chosen to use some of the new language features. I may at some point look at adding support for .Net 2.0.
Hi ..
This dll is not working with WindowService to Window application communication or vice-versa. so.. plz suggest me what should i do to communicate both each other...
It should work in Compatibility mode. Email me if you are having problems.
Hi there...
thank u so much... it has been done... it was my mistake bcoz i was using window messaging rather than IOStream.. tht's why i was unable to do it. .. but now using IOStream i am able to communicate thru window service. so thank you very much...
B R
The amazon transport is a nice feature but it increased the distribution size nearly 10x! This is extremely difficult when all I am using it for local IPC communication. Do you have a branch with it removed? If not I would like to submit my modified project as a branch. I have removed everything except what is required to support local communications.
Yes I think that's the Amazon SDK! I could look at stripping that back, I'm just embedding the unmodified lib at the moment.
The idea of the new architecture is that you don't need all of the DLLs. If you're not using Amazon Transport, just remove the reference and don't include in your distribution.
As transports are loosely coupled, you can remove these references without any impact. Drop me a mail if you have any more questions/problems.
Post a Comment