I have COM server with events implemented in C# and don't know how to write a C# client which hooks to events. I found several articles which describe how to do C# server and C++ clients but none (or I'm blind :-P ) which describe how to do the C# client using events. I'm able to connect to the COM server object but have no idea how to hook to the events.
Note I have two applications - one contains the C# COM server and another application which contains the C# client. The server is implemented as follows:
[ComVisible(true)]
[Guid("08214B02-512D-4785-9176-C4B4324FC340")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IMyServer
{
bool Play(string sFile);
}
[ComVisible(true)]
[Guid("141CAAEA-63CE-422E-BF00-BAF4DBEEA77A")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMyServerEvents
{
[DispId(1)]
event OnPlayFinishedHandler OnPlayFinished;
}
[ComVisible(true)]
[ProgId("MyApp.MyServer")]
[Guid("D184855D-E425-46A6-9171-34C828353778")]
[ComSourceInterfaces(typeof(IMyServerEvents))]
[ClassInterface(ClassInterfaceType.None)]
public class MyServer : IMyServer, IDisposable
{
public MyServer()
{
}
[ComVisible(false)]
public delegate void OnPlayFinishedHandler();
public event OnPlayFinishedHandler OnPlayFinished;
public bool Play(string sFile)
{
if (OnPlayFinished != null)
OnPlayFinished();
return true;
}
}
and now the client, I'm obtaining the COM server object via Running Object Table (not important I think here how).
IMyServer oServer = GetServer();
// TODO: Connect to OnPlayFinised event
oServer.Play("C://File.txt");
I tried to cast oServer to IMyServerEvents but it is not allowed.
As Hans said, you cannot add a COM reference if it's a .NET assembly. You have to add it as a normal assembly reference. Have you seen this MSDN example of implementing a C# COM server with events:
http://msdn.microsoft.com/en-us/library/dd8bf0x3(v=vs.90).aspx
Following from that MSDN example, you would then add that C# assembly as a reference in your C# client app. To hook into the events that the COM server raises, you'd write the event handler and then subscribe to the event. This sample code refers to the above MSDN example.
public void ClickEventHandler(int x, int y)
{
// The "Click" event was raised on the COM server, handle the event here
}
EventSource.Button myButton = new EventSource.Button();
myButton.Click += new EventSource.ClickDelegate(ClickEventHandler); // subscribe to the event
Related
I am working on a project that has a WPF and a mobile app, and there is also a .net standard library which has all the service and data layer which is shared between the two projects. I need to add a IConnectivityService Interface in the this common library project so that, before making a call to the server I can check if there is a connection available, and I also have an event handler in this interface that gets called when there is a change in connectivity.
public interface IConnectivityService
{
bool IsConnected { get; }
event EventHandler<ConnectivityChangedEventArgs> ConnectivityChanged;
}
The event handler for the Xamarin side do look like the above.
public interface IConnectivityService
{
bool IsConnected { get; }
event EventHandler ConnectivityChanged;
}
And for WPF it need to be this way.
Note: ConnectivityChangedEventArgs is a xamarin implementation and I would want to avoid from the shared code
Whats the best way to handle this? I ideally would like to have this as a shared code, so that I can use it in my service layer
I'm building a dll that will be used from wpf and other kind of framework (windows form, asp...). For this reason I don't want to use Messagebox. Which is the best way to send notification from dll to app and each decide the way to show the message to user (and wait an answer from user)? Somebody can help me to find the correct way?
Unless the library (.dll) is only intended to work with a particular UI, the library shouldn't "decide" how or if notifications get displayed. It's a separation of concerns. If a library determined that it should show a MessageBox then you wouldn't be able to use that same library with a web app or some out-of-sight service.
Here are two ways (not exhaustive) that we might get information from a separate library, including our own:
We call a function and the library returns a response. For example, it might indicate that an action succeeded or failed. The library doesn't know what type of app it's being called from or whether anyone needs to see the response. It just returns it. Your app can then receive that result and display a message.
A class within the library raises an event which indicates that something has happened. Same thing - it doesn't know what is listening for that even or what will happen as a result. It just raises the notification. Our app determines that in response to that event it should display a message.
When our libraries work that way they are easier to test using automated tests like unit tests and integration tests. It's easy to write a test which verifies that calling a method returns a certain result. It's much harder to verify that a MessageBox pops up.
And, as mentioned, it makes it more likely that we can use more of our code with different types of user interfaces. For those reasons it's beneficial to write as much of our code as possible in isolation from any UI, which means not including input/output behaviors that are specific to one type of UI.
You could expose an event that the consumers can subscribe to. Here is the general pattern to do this kind of thing:
You can create your own class to carry the data about the event:
public class NotificationEventArgs : EventArgs
{
public NotificationEventArgs(string message)
{
Message = message;
}
public string Message { get; }
}
You then create a delegate to represent the signature of the event:
public delegate void OnNotificationEventHandler(SomeClass sender, NotificationEventArgs args);
Your class or classes can then expose this delegate as an event:
public class SomeClass
{
private OnNotificationEventHandler _notificationEventHandler;
public event OnNotificationEventHandler OnNotification
{
add { _notificationEventHandler += value; }
remove { _notificationEventHandler -= value; }
}
protected void RaiseNotificationEvent(NotificationEventArgs args)
{
_notificationEventHandler?.Invoke(this, args);
}
public void SomeMethod()
{
//Your class does something that requires consumer notification
var args = new NotificationEventArgs("Something happened!");
//Raise the event for the consumers who are listening (if any)
RaiseNotificationEvent(args);
}
}
Finally, your consuming classes will subscribe to this event:
SomeClass obj = new SomeClass();
obj.OnNotification += Obj_OnNotification;
private static void Obj_OnNotification(SomeClass sender, NotificationEventArgs args)
{
//Handle the notification from the class here.
Console.WriteLine(args.Message);
}
The general idea is that consumers of your class only need to know that something has happened as well as details of what happened. How that event is consumed, handled or displayed is not the responsibility of your component.
I am trying to extend an application which supports COM/ActiveX objects. The COM dll needs to send some data to other system on local network for further processing and actions.
I have tested a basic WCF Host-Client setup and it works fine from console client to console host. But now I need to send data through a client in com-visible dll.
This is the code of the dll :
namespace Client
{
[Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]
[ComVisible(true)]
public interface ISend
{
[DispId(1)]
bool SendData(string msg);
}
[ClassInterface(ClassInterfaceType.None), Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"), ProgId("Client.Send")]
[ComVisible(true)]
public class Send : ISend
{
static BasicHttpBinding binding = new BasicHttpBinding();
static EndpointAddress endpoint = new EndpointAddress(new Uri("http://192.168.1.6:8000/WCFHost/Service/GetData"));
GetDataClient client = new GetDataClient(binding, endpoint);
[ComVisible(true)]
public bool SendData(string msg)
{
try
{
if (client.getData(msg))
{
client.Close();
return true;
}
else
{
client.Close();
return false;
}
}
catch (Exception e)
{
client.Abort();
return false;
}
}
}
}
The dll works fine as a reference but cannot create object through target application(It has the functionality to access COM/ActiveX objects). When I try to access the dll by :
obj = CreateObject ("Client.Send");
obj.SendData("Hello")
It says :
COM/object handle is null
on second line nothing more!
I created a com-visible dll in similar way using Remoting to achieve this and it worked like a charm. But now its not working as a WCF Client.
It would be really appreciated if someone could point out what I am doing wrong.
I had moved to Remoting where this was not a problem, but I was suggested to stay away from it and achieve this through WCF.
P.S : I am new to C# so please excuse any stupid mistakes.
COM does not support static methods, see here for further details. You'll need to remove the static keyword from the class in order to let your client create an instance. This will also allow you to implement the interface, which is not possible for static classes.
As a side note, your code shouldn't even compile, since the static modifier on an interface is illegal. Remove it as well, then recompile and re-register your DLL.
I'm trying to work with an old com control (a control array), the following samples: 5435293, 39541, 5497403, 5738092 explain (or at least what I understand) how to handle events of control arrays with .net controls, so they have Sender and EventArgs.
My question will be: How can you handle the events of an old com control array?.
EDIT:
The array will be created dynamically at the start, for example: Q. How many connections do you want? A. 5
example:
the control has this event: control_connected(int status, string description)
I can make some function with the same arguments and asign it to the connected event, but i cant figure out how to do it with a control array.
Ty so much for your help, and sorry about the crappy English... I'm not a navite English speaker
COM events have a different modal, you do not have one handler per event, you have an event sink object that hooks every event the COM server plan to raise. If you just hook the ActiveX events with delegates, event sink RCWs will be created and may cause crashes later, so I assume you are creating your own event sink class.
Since you have your own event sink class, you must follow the event publisher's event signature. The signatures do not have a sender argument, since the COM server assumes you have a reference to the sender, thus there is no need to send it again every time an event is raised.
You can, of cause, cache the server's reference in your event sink object for later use. Your event sink object can declare its own version of managed events with a sender parameter, and pass the cached COM server as the sender argument when it raises events.
Something like
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[TypeLibType(TypeLibTypeFlags.FHidden)]
[Guid("eventGuid")]
[CLSCompliant(false)]
public interface IEvent
{
[DispId(123)]
void control_connected(int status, string description);
}
public class EventSink:IEvent
{
object control;
public EventSink (object control)
{
this.control=control;
}
public event EventHandler<ControlConnectedEventArgs> ControlConnected;
void control_connected(int status, string description);
{
EventHandler<ControlConnectedEventArgs> temp=this.ControlConnected;
if(temp!=null)
temp(this.control, new ControlConnectedEventArgs(status,description));
}
}
If you have an array of COM servers, just declare an array of event sinks, attach each sink to each COM server with ConnectionPointCookie, and wire the event handlers from the event sink instead of the COM servers.
I'm trying to get access to certain parts of my application through COM, mainly because I need a Delphi application to be able to invoke certain services on the .NET application.
I've managed to expose some methods via COM inheriting from ServicedComponent class and its working fine.
Now I need to be able to raise events to the client so I can alert him when certain things occur. I currently have something like this:
public interface IEvents
{
void OnMessage();
}
[EventClass]
[ComVisible(true)]
public class MyEventsClass : ServicedComponent, IEvents
{
public void OnMessage()
{
}
}
but when I import the TypeLibrary from Delphi I get this:
IEvents = interface(IDispatch)
['{E7605303-F968-3509-829B-BF70084053C4}']
procedure OnMessage; safecall;
end;
IEventsDisp = dispinterface
['{E7605303-F968-3509-829B-BF70084053C4}']
procedure OnMessage; dispid 1610743808;
end;
which I don't really know how to use. I was expecting an IUnknown interface that I can implement and provide to the service through a MyService.Register(IEvents events)...
I think I'm completely lost here... Any advice or reference about how to properly implement COM+ events with .NET?
I'm still unsure of what exactly is the use for EventClass by I've discovered you don't need to use it. I simply have declared this:
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMyEventIface
{
}
And with that the interface is exported as an IUnknown interface to Delphi.
Then you define a method on your ServicedComponent class which a connect signature like this:
public void Connect(IMyEventIface eventHandler)
{
mEvents.Add(eventHandler);
}
which left you to manually handle the invoke of each client event handler but is the only way I've found.