VB6/COM Interop: where do these events come from? - c#

I have written a COM-visible class library in C# 4.0 which I'm consuming with VB6. The thing works, only if I open up the VB6 object browser and look at the members exposed, I'm seeing an event for each and every single exposed member... but the C# code doesn't define any of them.
Is this normal? Am I doing something wrong?
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IMyClass))]
public class MyClass : IMyClass
{
public void DoSomething(string someParam)
{
...
}
}
public interface IMyClass
{
void DoSomething(string someParam);
}
The assembly is signed with a strong name key and AssemblyInfo.cs has the [assembly: ComVisible(true)] attribute set, but I'm not sure it has anything to do with the issue.
When I look at the object browser in VB6, I would be expecting to see DoSomething(string) as a member of MyClass, and I do, however I'm also seeing an event with a matching signature for every exposed method, like Event DoSomething(someParam As String) as a member of MyClass.
Even more puzzling (to me at least), properties also have a "matching" event (can only tell from the little lightning icon though) - if MyClass defined a property like this:
public string SomeProperty { get; set; }
The VB6 object browser would say the "event" is defined as Property SomeProperty As String, which leaves me flabbergasted - how does a "property" 1) gets duplicated and 2) the duplicate gets displayed with an "event" icon in the object browser? The same applies to get-only properties, which have their read-only "property/event" counterpart.
Where do these events come from and how do I get rid of them?
UPDATE An image is worth a thousand words:
UPDATE The wrong thing was the ComSourceInterfaces attribute which was mistakenly being used in place of a ComDefaultInterface attribute. Swapping the former for the latter gives the expected result:

By passing typeof(IMyClass) as an argument to the ComSourceInterface attribute you're saying that everything in the IMyClass is an event.
If you don't want an event interface for your class remove the ComSourceInterface attribute.
If you do want to expose events from your C# class to VB then do the following:
When you create a COM visible class you'll also want to create an interface that defines just the event handlers for your class. Your class should be decorated with the COMSourceInterface specifying your event handler interface and should define your events and implement the event handler interface. See How To: Raise Events Handled by a COM sink for another example.
[GuidAttribute("1A585C4D-3371-48dc-AF8A-AFFECC1B0967") ]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface MyEvents
{
void ConnectedEvent(string state);
}
[ComSourceInterfaces(typeof(MyEvents))]
public class MyClass
{
public event Action<string> ConnectedEvent;
public MyClass() { }
public void DoSomething(string state)
{
if (ConnectedEvent != null)
ConnectedEvent(state);
}
}
See also: Murat's Corner: Exposing COM Events

You are basically finding out that there isn't anything special about events in COM. Like anything in COM, events are backed by an interface. The only thing special about an interface that specifies event methods is that it is marked with the [source] attribute in the type library. Which is all that the [ComSourceInterfaces] attribute does, recognized by Tlbexp.exe when it generates the type library.
Nor is there anything particularly special about properties in COM. They work just like they do in .NET, they are implemented with methods. A getter and a setter method.
So VB6 looks at your type library and is happy about a class that has events since it has an interface with the [source] attribute. And is happy about that interface having methods, all they can ever have, so it assumes those are the methods that run when the event is raised. It isn't otherwise smart enough to recognize that those methods are also the accessors for a property, it assumes that the type library author knows what he's doing.
Events are called "connection points" in COM. Google IConnectionPoint to learn more about it. If you ever create a WinRT component with custom event accessors then you'll also see that COM events have little in common with .NET events.
Anyhoo, the workaround is simple, only use [ComSourceInterface] when you raise events.

Related

How to assign the UIPopoverPresentationControllerDelegate

I try to use the UIPopoverPresentationController. For the presenting I need to assign the delegate (UIPopoverPresentationControllerDelegate) to be able to use methods like prepareForPopoverPresentation,
popoverPresentationControllerDidDismissPopover and so on.
If assign a view controller to the delegate property I get
Error CS0266 Cannot implicitly convert type 'UIKit.UIViewController' to 'UIKit.IUIPopoverPresentationControllerDelegate'. An explicit conversion exists (are you missing a cast?)
So what are my options here? In the native world one could add the required protocoll, but you can't do that in Xamarin. I want that the view controller which is presented in the popover gets the notification (prepare, did dismiss, ...). How can I do that?
Of course I could create a new instance of UIPopoverPresentationControllerDelegate and assign it, but then I would have connect the delegate with the view controller somehow (e.g. through events). Is there a simpler solution?
Edit
It looks like Xamarin has not exposed a WeakDelegate property and has also made the delegate class the abstract class for the protocol and not the interface. In this scenario - you will have to create another class that implements the UIPopoverPresentationControllerDelegate and another for the UIPopoverControllerDelegate.
In the future, it's worth noting that there is often a WeakDelegate property (along with Delegate) which allows assigning any class to the delegate and implementing the protocol implicitly through ExportAttribute on the protocol methods. Xamarin also sometimes uses the protocol's matching interface (such as IUIPopoverPresentationControllerDelegate) instead of the matching abstract class (the UIPopoverPresentationControllerDelegate class).
Mostly incorrect original answer
It should be possible for your UIViewController class to implement the IUIPopoverControllerDelegate and implement any methods that you need. Usually iOS protocols get converted into an interface for Xamarin.iOS and you can find it by appending an "I" to the protocol name.
public class TestVC : UIViewController, IUIPopoverControllerDelegate
{
public override UIPopoverPresentationController PopoverPresentationController {
get {
return base.PopoverPresentationController;
}
}
[Export ("popoverControllerDidDismissPopover:")]
public void DidDismiss (UIPopoverController popoverController)
{
throw new NotImplementedException ();
}
}
Here is an example where I've implemented a few of the popover methods. I hope this helps. I haven't explicitly tested this, but I think it should work. Let me know if it doesn't.
My solution was the following:
I subclassed UIPopoverPresentationControllerDelegate and here I defined the events PrepareForPopover and DidDismiss. So I overwrote PrepareForPopoverPresentation and DidDismissPopover and I throw the belonging events.
Then I assigned this delegate to my UIPopoverPresentationController instance. Through subscribing to the events of my custom delegate I was able to get notified, when the popover is displayed and when it is dismissed. Casting is not possible (as I tried in my question).
With this one can set some variables or call some methods from the view controller, which is presented in the popover.

Extend a VB6 interface with events in c#

Hi i have a vb6 interface called CLSFW_SBSession with an event:
Public Event DataTransfer(ByVal Data As Variant)
Public Property Get ActionID() As Long
End Property
...
I need to extend it with a c# interop class. I tried with:
[ComVisible(true)]
[Guid("ddb976bd-fe29-44d5-a163-e7780a4bb897")]
public class ClsSbSession : FWBO_LibSrv.CLSFW_SBSession
public event __CLSFW_SBSession_DataTransferEventHandler DataTransfer;
...
Now if i declare a object of this type in vb6 and i instance it, without With events, it works:
Private session As FWBO_LibSrv.CLSFW_SBSession
Set session = new ClsSbSession()
But if i use:
Private WithEvents session As FWBO_LibSrv.CLSFW_SBSession
and i try to instance it:
Set session = new ClsSbSession()
I receive an error:
Object or class does not support the set of events
How can i do for manage events from vb6 of my c# class? thanks
i have a vb6 interface
This is pretty murky, VB6 does not permit declaring an interface. It does generate them from a VB6 Class declaration, their name start with an underscore. There should be two, one for the class and probably named _CLSFW_SBSession, another for the event. One way to find these interface names is by running Oleview.exe from the Visual Studio Command Prompt. Use File + View Typelib and select the VB6 executable. Find the coclass declaration and note the interfaces marked by the [default] and [source] attributes.
You need to derive your class from the [default] interface. And you must use the [ComSourceInterfaces] attribute to name the [source] interface. The MSDN howto page is here. It isn't otherwise clear why you are trying to mimic the VB6 class, that is only necessary if you need a drop-in replacement for the existing VB6 class and need to keep legacy client programs running. That has additional requirements, like matching the guids.

Does every little event published to an Event Aggregator need to be an empty CompositePresentationEvent of its own?

I am trying to understanding the Event Aggregator pattern from an architecture and design view point. I have never used Prism in WPF before, but I'm studying how it works on MSDN.
It seems to me that for every event, the user has to create a new event object that extends the CompositePresentationEvent. It also appears that the new event object has no functionality other than those it inherited from (it usually has no code for itself).
So for example:
A AddNewStuffEvent would look like:
public class AddNewStuffEvent : CompositePresentationEvent<Object> {} //The end of the class
For a HealthChangeEvent:
public class HealthChangeEvent: CompositePresentationEvent<Object> {} //The end of the class
For a BookFlipEvent:
public class BookFlipEvent: CompositePresentationEvent<Object> {} //The end of the class
For a BookCloseEvent:
public class BookCloseEvent: CompositePresentationEvent<Object> {} //The end of the class
And this can go on forever for every little small event for BookOpenEvent, BookTearEvent, etc. So, in a particular namespace folder, there will be a whole ton of event classes, and the Event Aggregator is going to be loaded with all these event objects during runtime. That's, every little small event needs an empty class? Is this how it works? What could be a better way for this?
Yes, every event type needs its own class, which you have to define.
It also appears that the new event object has no functionality other than those it inherited from
The purpose is simply to provide strong typing for the event. This makes it easier to write code to subscribe to them. Ie, the subscribing code can be written like:
aggregator.GetEvent<AddNewStuffEvent>().Subscribe(Handler);
This is a preferable approach to alternatives, such as reliance on "magic strings" in the form of say aggregator.GetEvent("AddNewStuffEvent").Subscribe(Handler) (which could not be verified at compile time.

Onscroll event handling in C# BHO for IE9

I’m working on C# BHO plug-in for IE. Plug-in supposed to react on scroll event. Code bellow responsible for it:
var document = (HTMLDocument)webBrowser.Document;
((HTMLWindowEvents2_Event)document.parentWindow).onscroll += WebBrowserWindowOnScroll;
This approach works pretty good in IE7 and IE8. But completely useless in IE9.
I have found this workaround:
http://social.msdn.microsoft.com/Forums/et-EE/ieextensiondevelopment/thread/808df95a-c559-44c3-93b7-b9e3b2c3b737
It seems that it should solve problem but unfortunately it on C++ and I failed to move it on C#.
Can someone suggest workaround for IE9 or how to implement approach mentioned above on C#?
Thanks so much!
I managed to find the solution.
IHTMLWindow3 has a method attachEvent which requires name of the event as a first argument (“onscroll” in my case) and object which will be responsible for event handling. The trickiest part is connected with this handler object. It should implement IDispatch interface, but IE9 use this interface in a pretty bizarre way. It calls IDispatch.Invoke without specifying a method name which should be called. .NET automatically implements IDispatch when class marked by [ClassInterface(ClassInterfaceType.AutoDispatch)] attribute, and uses reflection to call its instance methods according to arguments of IDispatch.Invoke. In our case method name is empty so nothing will be called. [DispId(0)] attribute allows to solve this problem, it specifies method what should be called if Invoke receives empty method name.
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class EventListener
{
[DispId(0)]
public void HandleEvent(object target)
{
}
}
It should be mentioned that name of handler method doesn’t matter. But its signature is important. f.e. for ‘onscroll’ event it should be like shown above, ‘onclick’ handler requires no arguments etc.

Generate intercepting proxy from concrete class?

I need to create a proxy which intercepts properties in a class. I know how to create a dynamic proxy with Emit from an interface, but what if I don't have an interface? I've seen samples which use RealProxy (like this one: Is there a way to call a method when any property of a class is set?) but is it possible to use type generation and emit to achieve the same thing? I don't want the "owner" of the concrete class to see any traces of MarshalByRefObject if possible (see below)...
I believe Castle is able to do this, but maybe it's using RealProxy under the covers?
User user = Create<User>();
public class User
{
public string Name { get; set; }
}
public T Create<T>()
{
//magic happens here... :)
return (T)GenerateInterceptingProxyFromT(typeof(T));
}
I just started messing with postshrp, one of the AOP tools Miguel mentioned, do functionally what you are trying to do. It uses "static weaving" to inject code at compile time so should be invisible to consumers. Obviously, you need to modify the code that you want to instrument for this to work.
The answer to This question suggests using the profiler API which may be an option for you if PostSharp, or Castle won't do what you need.
There are some options on intercepting things in .Net:
If it is an interface, you can implement a new type from that dynamically, and make a proxy, that would re-call the other inner object.
If it is an abstract class or a class that allows overrides, you can inherit from it and override the desired members dynamically, and do whatever you want.
If the type you want to intercept has no interfaces, nor overridable methods, or properties, then you must change the assembly that constains that type, before it loads. You cannot change the code of an assembly after it has been loaded. I think that PostSharp works this way.
Most of the mocking tools, used for testing purposes uses the first/second alternatives, but that makes them work only with member of the classes that are overridable, or implemented through an interface.
Aspect Oriented Programming tools use the third alternative, but it is more work to do, because you need to process the assembly before it is loaded.
Since this is a very common problem and a great reason to choose an AOP approach as Miguel suggested, I created an example for Afterthought that demonstrates implementing INotifyPropertyChanged (intercepting property sets to raise an event).
Afterthought lets you describe interceptions for properties very easily, and specifically makes property set interception simple by providing you the before and after values of the property. You would do something like this to identify the properties to intercept:
public override void Amend<TProperty>(Property<TProperty> property)
{
// Raise property change notifications
if (property.PropertyInfo.CanRead && property.PropertyInfo.CanWrite)
property.AfterSet = NotificationAmender<T>.OnPropertyChanged<TProperty>;
}
Which in this case calls a static method OnPropertyChanged which looks like this:
public static void OnPropertyChanged<P>(INotifyPropertyChangedAmendment instance, string property, P oldValue, P value, P newValue)
{
// Only raise property changed if the value of the property actually changed
if ((oldValue == null ^ newValue == null) || (oldValue != null && !oldValue.Equals(newValue)))
instance.OnPropertyChanged(new PropertyChangedEventArgs(property));
}
So if your original property looked like this:
string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
It would look like this after applying the above amendment using Afterthought:
string name;
public string Name
{
get
{
return name;
}
set
{
string oldValue = Name;
name = value;
NotificationAmender<ConcreteClass>.OnPropertyChanged<string>(
this, "Name", oldValue, value, Name);
}
}
In your case, the static method called after (or before) the setter could be named anything you want and do anything you want. This is just an example of a concrete and well known reason to intercept property setters. Given that you know the properties are non-virtual, it is not possible to create proxy subclasses to perform the interception, so I think AOP approaches like Afterthought or PostSharp are your best bet.
Also, with Afterthought you can implement the interception such that the resulting assemblies do not have any references or dependencies on Afterthought and if your interception logic does not actually add/change the API for your target types, there is no reason the "owner" of the concrete class would have a problem with the result.

Categories