How to prevent InitializeComponent code from constructing a member field / property - c#

Firstly I'm very new to WPF (coming from WinForms) and I'm facing quite a difficult problem. Sorry if it's rather lengthy, I've tried to give it structure and shorten it down.
Environment (brief)
I'm using a custom user control CaptureControl inside my MainWindow. This control has an encapsuled field / property of an InputManager object. This InputManager object is instantiated a single time in the global App class. It is later assigned to user control members (e.g. the CaptureControl).
Problem
My problem with this is that for some reason the InitializeComponent() call inside my CaptureControl constructs an instance of InputManager somewhere in external code. The InputManager registers event handlers to a static class during construction and needs to be disposed in order to unregister these handlers again. Sadly whatever constructs an instance of the InputManager class does not dispose it properly again or keeps it referenced at some place where I can't access it. The _InputManager field remains being null.
This is the problematic code:
public partial class CaptureControl : UserControl
{
/// <summary>
/// <para>Gets or sets the Input Manager to be used</para>
/// </summary>
public InputManager InputManager
{
get => _InputManager;
set
{
_InputManager = value;
lstTriggeredKeys.DataContext = _InputManager;
}
}
private InputManager _InputManager;
public CaptureControl()
{
InitializeComponent(); // This calls external code which constructs an instance of InputManager
}
}
This invokes the constructor of my InputManager class:
public class InputManager : IDisposable
{
/// <summary>
/// <para>Constructs the input helper</para>
/// <para>Registers static keyboard events</para>
/// </summary>
public InputManager()
{
Interceptor.OnKeyInput += Interceptor_OnKeyInput;
}
/// <summary>
/// <para>Releases static keyboard events</para>
/// </summary>
public void Dispose()
{
Interceptor.OnKeyInput -= Interceptor_OnKeyInput;
}
private void Interceptor_OnKeyInput(KeyInput input)
{ ... }
}
Which results in two problems:
There is an event handler unnecessarily being invoked on an anonymous object
The dependent clean-up logic on the static class is not fired if the event handler count is higher than 0
Question
I have difficulties understanding why the InitializeComponent() call would construct an instance of InputManager in the first place.
How can this be prevented? How can I prevent the external code behind
InitializeComponent() to construct an instance of
InputManager for whatever reason?
Call Stack
The external code call stack behind InitializeComponent() is huge and ends with an invisible native to managed transition. The last visible record before reaching the InputManager constructor is:
> InputData.dll!InputData.InputManager.InputManager() Line 40 C#
> [Native to Managed Transition]
> [Managed to Native Transition]
> mscorlib.dll!System.RuntimeType.CreateInstanceSlow(bool publicOnly, bool skipCheckThis, bool fillCache = true, ref System.Threading.StackCrawlMark stackMark)
> ...
> InputH.exe!InputH.CaptureControl.InitializeComponent() Line 1 C#
> ...
Environment (detailed)
The App class which inherits the System.Windows.Application class and constructs the InputManager object. The object is assigned to one or more user controls later on:
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
// Single instance / global / shared object
public InputManager InputManager { get; private set; } = new InputManager();
// Disposing of the object
protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
InputManager.Dispose();
}
}
The global InputManger instance is assigned to the user control in the Constructor of my MainWindow. It also assigns the shared single instance InputManager object to it:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
captureControl.InputManager = ((App)Application.Current).InputManager;
}
}

Related

How can i get the type of a Unity UI element as a generic parameter?

Im having a UIManager class, that is static. In this class, I tried to make functions that I can call wherever I want, and they shows or hides the UI elements of which name i gave it to the function.
My class looks like this:
public static class UIManager
{
//If you deactivate a gameobject at the Editor, you cant activate it later. This is where UI handler comes in.
//It hides or shows the UI elements the game has
/// <summary>
/// Shows the UI element of the given name
/// </summary>
/// <param name="name"></param>
public static void ShowUIElement(string name)
{
GameObject.Find(name).SetActive(true);
}
/// <summary>
/// Shows the UI Elements that are in the array
/// </summary>
/// <param name="names"></param>
public static void ShowUIElement(params string[] name)
{
foreach (var item in name)
{
GameObject.Find(item).SetActive(true);
}
}
/// <summary>
/// Hides the UI element of the given name
/// </summary>
public static void HideUIElement(string name)
{
GameObject.Find(name).SetActive(false);
}
/// <summary>
/// Hides the UI Elements that are in the array
/// </summary>
/// <param name="name"></param>
public static void HideUIElement(params string[] name)
{
foreach (var item in name)
{
GameObject.Find(item).SetActive(false);
}
}
Somehow it is not working. Maybe if I deactivate a UI element, I can't activate it, because it does not exist anymore. In Unity, the UI elements has a ".enabled" property that is changeable, but I have to know what type of UI element (button, text, panel, canvas) is, to make it disabled. This is my problem. I need a generic solution for all type of UI element, so for the function, I just pass the name of the UI element that i need to activate or deactivate, plus the type of it, so I dont have to make a function for every UI element type. Can you help me?
If you need to enable or disable components, then you can use Behaviour class, because all components that can be enabled or disabled derives from it. This can be applied to all components, not only UI.
private Image image;
private Button button;
private Text text;
private Canvas canvas;
private void Start()
{
DisableUIElement(image);
DisableUIElement(button);
DisableUIElement(text);
DisableUIElement(canvas);
}
void DisableUIElement(Behaviour uiElement)
{
uiElement.enabled = false;
}
But don't use find by name, it is very slow. Make references to your elements and pass them.

Problem with an object pooling mechanism, Queue containing the pool can't be static because static variables aren't inherited

I'm setting up an object pooling mechanism in unity, basically how it works is:
You have a base abstract class called "Poolable", with a static queue containing objects in it (In this case GameObjects), there's a class called Projectile which inherits from Poolable as that's what i currently want to be pooled. It further extends to a Bullet class which inherits from Projectile, but the problem is, if i had an Arrow class which inherits from Projectile, it would use the same pool because the pool is static inside Poolable. Does anyone know a way i could tackle this issue?
I thought about making it non-static, but then the objects wouldn't know about the pool, they'd know about their own pool. Same with interfaces but they don't accept variables which aren't properties
The only current fix i see is adding it to each script that i use a pool on, but that then defeats the whole purpose of inheritance as i was trying to make a polymorphic-like structure where you'd have multiple poolables, multiple projectiles, maybe multiple bullets/arrows?
public abstract class Poolable : MonoBehaviour
{
protected static Queue<GameObject> objPool = new Queue<GameObject>(); // doesn't work because every poolable object will use this pool only
[SerializeField] protected GameObject prefab;
public GameObject Get()
{
if(objPool.Count == 0)
{
AddObjects();
}
return objPool.Dequeue();
}
protected void ReturnToPool(GameObject retObj)
{
objPool.Enqueue(retObj);
retObj.SetActive(false);
}
AddObjects()
{
}
}
If I am not mistaken, you need your object pool to be homogeneous. This means your pool contains only one type of object. For example, you don't want arrow and bullet to share the same pool even if they are all projectiles. We don't have c++'s decltype in c#, so if you want your object to have it's non-static member function ReturnToPool, you have to defer type evaluation to run-time (using a type dictionary). Here is the code that would possibly satisfy your need:
using System.Collections.Generic;
using UnityEngine;
using System;
public abstract class Poolable : MonoBehaviour
{
private static Dictionary<Type, Queue<Component>> objPool
= new Dictionary<Type, Queue<Component>>();
/// <summary>
/// Get an object from the pool; If fails, use the alternative method to create one
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="alternativeCreate"></param>
/// <returns></returns>
public static T Get<T>(Func<T> alternativeCreate) where T : Poolable
{
if (objPool.TryGetValue(typeof(T), out var queue) && queue.Count > 0)
{
var ret = queue.Dequeue() as T;
ret.Reactive();
return ret;
}
return alternativeCreate();
}
/// <summary>
/// Return the object to the pool
/// </summary>
public void ReturnToPool()
{
if (this.Reset())
{
var type = this.GetType();
Queue<Component> queue;
if (objPool.TryGetValue(type, out queue))
{
queue.Enqueue(this);
}
else
{
queue = new Queue<Component>();
queue.Enqueue(this);
objPool.Add(type, queue);
}
}
}
/// <summary>
/// Reset the object so it is ready to go into the object pool
/// </summary>
/// <returns>whether the reset is successful.</returns>
protected virtual bool Reset()
{
this.gameObject.SetActive(false);
return true;
}
/// <summary>
/// Reactive the object as it goes out of the object pool
/// </summary>
protected virtual void Reactivate()
{
this.gameObject.SetActive(true);
}
}
You should look into the Singleton Pattern. It's hard to suggest a suitable implementation for what you need because I don't know what you want to do with it, but basically, you can add something like this to your class:
private static Poolable _instance;
private static object _instanceLock = new Object();
public static Poolable Instance
{
get
{
if (_instance == null)
{
lock (_instanceLock)
{
if (_instance == null)
{
this._instance = [Whatever way you instantiate it];
}
}
}
return _instance;
}
}
Afterward, either make the constructor private/protected and make sure you always get your instance with Poolable.Instance.
Hope it helps!

C#, Unity - Most convenient and fast way to persist/access/modify values from scene to scene?

I looked for answers, but still at loss here. I have some character generator, which creates then saves player character variables. They are saved into a fresh script "PlayerCharacterData", which is a component of object CharacterData. This object has DontDestroyOnLoad, so it persists to other scenes. After generating character, game loads character data into that script, and when I switch scenes, data is properly loaded from binary files (serialized earlier).
But now I have this core and I need my numerous UI objects to load data from "PlayerCharacterData" class, so UI can fill all fields, etc. Later whole game will be depending on variable values from "PlayerCharacterData".
This is part of my PlayerCharacterData script:
public class PlayerCharacterData : MonoBehaviour {
void Awake(){
DontDestroyOnLoad (this); // keeps the object when changing scenes
}
// below we have data stored in variables. It was loaded by another script.
public string characterName;
public int characterSpeed;
public List<Weapon>characterInvWeapon;
// etc, more variables x50-70
}
Now here's example of my public class UIPlayerCharacterData. I added a tag to object CharacterData in Unity Editor to make "Find" faster:
public class UIPlayerCharacterData : PlayerCharacterData {
public void NameToCharacterDataUI() {
// this would be required to make it work: PlayerCharacterData playerCharacterData = GameObject.FindGameObjectWithTag("CharacterData").GetComponent<PlayerCharacterData>();
Text_ch_sh_char_name_string.text = playerCharacterData.characterName;
}
// etc, more functions like that x50-70
void Awake () {
PlayerCharacterData playerCharacterData = GameObject.FindGameObjectWithTag("CharacterData").GetComponent<PlayerCharacterData>();
NameToCharacterDataUI();
// etc, calling more functions x50-70
}
}
Problem is, those classes are on different objects. First is component of CharacterData object which persists from 1st scene, second is component of major UI panel in 2nd scene. Second class has LOTS of UI fields to fill, just showed you one of those. Each takes data from first class (component on CharacterData object). So there's like 50-70 variables for UI to pull data from.
And that's just a start, because whole game will need to get and modify data in PlayerCharacterData script.
Now, I've found out the function NameToCharacterData() isn't working, because it has no reference in playerCharacterSheet variable: "Object reference not set to an instance of an object". Thought it was sorted out in Awake() - I was wrong.
So seems we'd need to set
PlayerCharacterData playerCharacterData = GameObject.Find("CharacterData").GetComponent<PlayerCharacterData>();
...in every of 50-70 functions filling the fields in UI.
Not to mention every other system in game would need to do that too.
Plus, everytime player opens the UI those UI fields need to be re-generated.
Is there some faster, more convenient way to do it?
I was thinking about making all PlayerCharacterData variables static - this would be fast as lightning, but I'd prefer to make our engine ready for multiplayer (between 1 and 200 players). So not sure.
Also, wouldn't it be better to save character data variables into some script without Monobehaviour, so not connected to a gameObject, and maybe then we could just use PlayerCharacterData playerCharacterData = new PlayerCharacterData(); to get values from that class? I simply need those values accesible really fast and from everywhere.
Really considering the use of static variables at this point, but if there's a better solution, or if there's a better way create static variables for a multiplayer game, let me know.
It's not clear to me where you're trying to store your PlayerCharacterData reference, but since you declared it locally inside Awake(), that won't be available outside that method. It's also not clear to me how Awake() isn't sufficient to store this reference, but even in that case, you could wrap all access to it in a "safe" method to avoid the null reference. Maybe something like this?
public class UIPlayerCharacterData : PlayerCharacterData {
PlayerCharacterData playerCharacterData;
public void NameToCharacterDataUI() {
Text_ch_sh_char_name_string.text = GetPlayerCharacterData().characterName;
}
// etc, more functions like that x50-70
void Awake () {
PlayerCharacterData playerCharacterData = GetCharacterData();
NameToCharacterDataUI();
// etc, calling more functions x50-70
}
PlayerCharacterData GetPlayerCharacterData() {
if (playerCharacterData == null) {
playerCharacterData = GameObject.FindGameObjectWithTag("CharacterData").GetComponent<PlayerCharacterData>();
}
return playerCharacterData;
}
}
With Singleton pattern, wich is a design pattern that restricts the instantiation of a class to one object, this way you only make one global Instance and accses it from diferent scripts.
MyClass Script:
public class MyClass : MonoBehaviour {
void Awake () {
Debug.Log(Manager.Instance.myGlobalVar);
}
}
Global Script:
public class Manager : Singleton<Manager> {
protected Manager () {} // guarantee this will be always a singleton only - can't use the constructor!
public string myGlobalVar = "whatever";
}
SingletonImplementation:
using UnityEngine;
/// <summary>
/// Be aware this will not prevent a non singleton constructor
/// such as `T myT = new T();`
/// To prevent that, add `protected T () {}` to your singleton class.
///
/// As a note, this is made as MonoBehaviour because we need Coroutines.
/// </summary>
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T _instance;
private static object _lock = new object();
public static T Instance
{
get
{
if (applicationIsQuitting) {
Debug.LogWarning("[Singleton] Instance '"+ typeof(T) +
"' already destroyed on application quit." +
" Won't create again - returning null.");
return null;
}
lock(_lock)
{
if (_instance == null)
{
_instance = (T) FindObjectOfType(typeof(T));
if ( FindObjectsOfType(typeof(T)).Length > 1 )
{
Debug.LogError("[Singleton] Something went really wrong " +
" - there should never be more than 1 singleton!" +
" Reopening the scene might fix it.");
return _instance;
}
if (_instance == null)
{
GameObject singleton = new GameObject();
_instance = singleton.AddComponent<T>();
singleton.name = "(singleton) "+ typeof(T).ToString();
DontDestroyOnLoad(singleton);
Debug.Log("[Singleton] An instance of " + typeof(T) +
" is needed in the scene, so '" + singleton +
"' was created with DontDestroyOnLoad.");
} else {
Debug.Log("[Singleton] Using instance already created: " +
_instance.gameObject.name);
}
}
return _instance;
}
}
}
private static bool applicationIsQuitting = false;
/// <summary>
/// When Unity quits, it destroys objects in a random order.
/// In principle, a Singleton is only destroyed when application quits.
/// If any script calls Instance after it have been destroyed,
/// it will create a buggy ghost object that will stay on the Editor scene
/// even after stopping playing the Application. Really bad!
/// So, this was made to be sure we're not creating that buggy ghost object.
/// </summary>
public void OnDestroy () {
applicationIsQuitting = true;
}
}
Requirement: MonoBehaviourExtended.cs (from GetOrAddComponent)
static public class MethodExtensionForMonoBehaviourTransform {
/// <summary>
/// Gets or add a component. Usage example:
/// BoxCollider boxCollider = transform.GetOrAddComponent<BoxCollider>();
/// </summary>
static public T GetOrAddComponent<T> (this Component child) where T: Component {
T result = child.GetComponent<T>();
if (result == null) {
result = child.gameObject.AddComponent<T>();
}
return result;
}
}
Source
Use a static class to store data. I prefer to use static class instead of Singleton "anti-pattern" if it is possible.
public static class Cache
{
/// <summary>
/// Actual cache storage for data.
/// </summary>
private static Dictionary<string, object> _cache = new Dictionary<string, object>();
/// <summary>
/// Adds/changes an object to the cache.
/// </summary>
/// <param name="key">The identifier of stored object to access it.</param>
/// <param name="obj">The object to store</param>
public static void Set(string key, object obj)
{
if (string.IsNullOrEmpty(key))
throw new Exception("key must be a non empty string");
if (_cache.ContainsKey(key))
_cache[key] = obj;
else
_cache.Add(key, obj);
}
/// <summary>
/// Get the cached object by key.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <returns></returns>
public static T Get<T>(string key)
{
var obj = _cache.ContainsKey(key) ? _cache[key] : null;
return (T)obj;
}
/// <summary>
/// Removes an object.
/// </summary>
/// <param name="key">The identifier of stored object</param>
public static void Remove(string key)
{
if (string.IsNullOrEmpty(key))
throw new Exception("key must be a non empty string");
if (_cache.ContainsKey(key))
_cache.Remove(key);
}
}

Trace PRISM / CAL events (best practice?)

Ok,
this question is for people with either a deep knowledge of PRISM or some magic skills I just lack (yet). The Background is simple: Prism allows the declaration of events to which the user can subscribe or publish. In code this looks like this:
_eventAggregator.GetEvent<LayoutChangedEvent>().Subscribe(UpdateUi, true);
_eventAggregator.GetEvent<LayoutChangedEvent>().Publish("Some argument");
Now this is nice, especially because these events are strongly typed, and the declaration is a piece of cake:
public class LayoutChangedEvent : CompositePresentationEvent<string>
{
}
But now comes the hard part: I want to trace events in some way. I had the idea to subscribe using a lambda expression calling a simple log message. Worked perfectly in WPF, but in Silverlight there is some method access error (took me some time to figure out the reason).. If you want to see for yourself, try this in Silverlight:
eA.GetEvent<VideoStartedEvent>().Subscribe(obj => TraceEvent(obj, "vSe", log));
If this would be possible, I would be happy, because I could easily trace all events using a single line to subscribe. But it does not... The alternative approach is writing a different functions for each event, and assign this function to the events. Why different functions? Well, I need to know WHICH event was published. If I use the same function for two different events I only get the payload as argument. I have now way to figure out which event caused the tracing message.
I tried:
using Reflection to get the causing event (not working)
using a constructor in the event to enable each event to trace itself (not allowed)
Any other ideas?
Chris
PS: Writing this text took me most likely longer than writing 20 functions for my 20 events, but I refuse to give up :-) I just had the idea to use postsharp, that would most likely work (although I am not sure, perhaps I end up having only information about the base class).. Tricky and so unimportant topic...
Probably the easiest thing would be to subclass CompositePresentationEvent and override the behavior of the Publish event. Here's the source for CompositePresentationEvent:
http://compositewpf.codeplex.com/SourceControl/changeset/view/26112#496659
Here's the current Publish behavior:
public virtual void Publish(TPayload payload)
{
base.InternalPublish(payload);
}
So you could just add a little to this:
public virtual override void Publish(TPayload payload)
{
ILoggerFacade logger = ServiceLocator.Current.GetInstance<ILoggerFacade>();
logger.Log("Publishing " + payload.ToString(), Category.Debug, Priority.Low);
base.InternalPublish(payload);
}
Here I'm using the logger facility built into Prism, but feel free to substitute your own (or better, just implement ILoggerFacade!).
I was surprised that there were any default messages being published or places to plug in tracing in this system... as much as EventAggregator is abused by people, you'd think this would be a big request!
A little late but better late than never! I recently had the same problem and this is how I solved it.
First, I didn't like the Prism method of publishing/subscribing to events, so I used a method like this instead:
http://neverindoubtnet.blogspot.com/2009/07/simplify-prism-event-aggregator.html
This post above suggests using Extension methods on Event Aggregator to simplify the call to publish/subscribe. As a result your client code looks like this:
IEventAggregator ev;
ev.Publish<MyCustomMessage>();
//or
ev.Publish(new MyCustomMessage(someData));
//and similarly subscription
ev.Subscribe<MyCustomMessage(this.OnCustomMessageReceived);
// ...
private void OnCustomMessageReceived(MyCustomMessage message)
{
// ...
}
// With a BaseMessageEvent class as follows (see the blog post above for where this comes from)
/// <summary>
/// Base class for all messages (events)
/// </summary>
/// <typeparam name="TMessage">The message type (payload delivered to subscribers)</typeparam>
public class BaseEventMessage<TMessage> : CompositePresentationEvent<TMessage>
{
}
Ok this is great, but rather than hacky extension methods I implemented my own event service as follows:
/// <summary>
/// The EventService instance
/// </summary>
public class EventService : IEventService
{
private readonly IEventAggregator eventAggregator;
private readonly ILoggerFacade logger;
/// <summary>
/// Initializes a new instance of the <see cref="EventService"/> class.
/// </summary>
/// <param name="logger">The logger instance.</param>
/// <param name="eventAggregator">The event aggregator instance.</param>
public EventService(IEventAggregator eventAggregator, ILoggerFacade logger)
{
this.logger = logger;
this.eventAggregator = eventAggregator;
}
#region IEventService Members
/// <summary>
/// Publishes the event of type TMessageType to all subscribers
/// </summary>
/// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
public void Publish<TMessageType>() where TMessageType : BaseEventMessage<TMessageType>, new()
{
TMessageType message = Activator.CreateInstance<TMessageType>();
this.Publish(message);
}
/// <summary>
/// Publishes the event of type TMessageType to all subscribers
/// </summary>
/// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
/// <param name="message">The message to publish</param>
public void Publish<TMessageType>(TMessageType message) where TMessageType : BaseEventMessage<TMessageType>, new()
{
// Here we can log our message publications
if (this.logger != null)
{
// logger.log etc..
}
this.eventAggregator.GetEvent<TMessageType>().Publish(message);
}
/// <summary>
/// Subscribes to the event of type TMessage
/// </summary>
/// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
/// <param name="action">The action to execute when the event is raised</param>
public void Subscribe<TMessageType>(Action<TMessageType> action) where TMessageType : BaseEventMessage<TMessageType>, new()
{
// Here we can log our message publications
if (this.logger != null)
{
// logger.log etc..
}
this.eventAggregator.GetEvent<TMessageType>().Subscribe(action);
}
#endregion
}
Then I register IEventService/EventService as a singleton in the bootstrapper and forget about using the IEventAggregator, just use this (however if someone uses the IEventAggregator, its the same instance as that used by the EventService so will still work).
Finally, another trick to add is to use the Stack Frame to tell me where publications and subscriptions are coming from. Note this is a slow process (unwinding the stack frame) so use it sparingly. If you are
raising an event regularly then perhaps put a flag in your BaseEventMessage and check that to see whether to log publications for certain event types.
// Inside Publish method ... Log the subscription
if (this.logger != null)
{
Type messageType = typeof(TMessageType);
Type callingType = GetCallingType();
string methodName = GetCallingMethod().Name;
// Log the publication of this event
this.logger.Log(
string.Format("Event {0} was published by {1}.{2}()",
messageType.Name,
callingType.Name,
methodName),
Category.Debug,
Priority.Low));
}
// Additional methods to add to EventService to get the calling type/class
//
/// <summary>
/// Gets the Type that called the method or property where GetCallingType is called
/// </summary>
/// <returns>The class type that called</returns>
[MethodImplAttribute(MethodImplOptions.NoInlining)]
public static Type GetCallingType()
{
int skip = 2;
MethodBase method = new StackFrame(skip, false).GetMethod();
return method.DeclaringType;
}
/// <summary>
/// Gets the Method that called the method or property where GetCallingMethod is called
/// </summary>
/// <returns>The method type that was called</returns>
public static MethodBase GetCallingMethod()
{
return new StackFrame(2, false).GetMethod();
}
Note the above won't work in Silverlight (the use of the StackFrame), but the rest does. I've found this invaluable when debugging the multitude of events flying around a Prism app!

raising a vb6 event using interop

I have a legacy VB6 component that I've imported into VS using tlbimp.exe to generate my interop assembly. The VB6 component defines an event that allows me to pass messages within VB6.
Public Event Message(ByVal iMsg As Variant, oCancel As Variant)
I would really like to be able to raise this even in my C# program, but its getting imported as an event, not a delegate or something else useful. So, I can only listen, but never fire. Does anyone know how to fire an event contained within VB6? The C# event looks like
[TypeLibType(16)]
[ComVisible(false)]
public interface __MyObj_Event
{
event __MyObj_MessageEventHandler Message;
}
I unfortunately cannot change the VB6 code. Thanks.
Actually, hope is not lost yet. It is possible to raise an event on a COM object from outside of the object's class. This functionality is actually provided by COM itself, although in an indirect manner.
In COM, events work on a publish/subscribe model. A COM object that has events (the "event source") publishes events, and one or more other COM objects subscribe to the event by attaching an event handler to the source object (the handlers are called "event sinks"). Normally, the source object raises an event by simply looping through all the event sinks and calling the appropriate handler method.
So how does this help you? It just so happens that COM lets you query an event source for a list of all the event sink objects currently subscribed to the source object's events. Once you have a list of event sink objects, you can simulate raising an event by invoking each of the sink object's event handlers.
Note: I'm over-simplifying the details and being liberal with some of the terminology, but that's the short (and somewhat politically incorrect) version of how events work in COM.
You can take advantage of this knowledge to raise events on a COM object from external code. In fact, it is possible to do all of this in C#, with the help of the COM interop support in the System.Runtime.Interop and System.Runtime.Interop.ComTypes namespaces.
EDIT
I wrote a utility class that will allow you to raise events on a COM object from .NET. It's pretty easy to use. Here is an example using the event interface from your question:
MyObj legacyComObject = new MyObj();
// The following code assumes other COM objects have already subscribed to the
// MyObj class's Message event at this point.
//
// NOTE: VB6 objects have two hidden interfaces for classes that raise events:
//
// _MyObj (with one underscore): The default interface.
// __MyObj (with two underscores): The event interface.
//
// We want the second interface, because it gives us a delegate
// that we can use to raise the event.
// The ComEventUtils.GetEventSinks<T> method is a convenience method
// that returns all the objects listening to events from the legacy COM object.
// set up the params for the event
string messageData = "Hello, world!";
bool cancel = false;
// raise the event by invoking the event delegate for each connected object...
foreach(__MyObj sink in ComEventUtils.GetEventSinks<__MyObj>(legacyComObject))
{
// raise the event via the event delegate
sink.Message(messageData, ref cancel);
if(cancel == true)
{
// do cancel processing (just an example)
break;
}
}
Below is the code for the ComEventUtils class (as well as helper class, SafeIntPtr, because I'm paranoid and wanted a nice way to deal with the IntPtrS needed by the COM-related code):
Disclaimer: I haven't thoroughly tested the code below. The code performs manual memory management in a few places, and therefore there is the possibility that it could introduce memory leaks into your code. Also, I didn't add error-handling to the code, because this is only an example. Use with care.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using COM = System.Runtime.InteropServices.ComTypes;
namespace YourNamespaceHere
{
/// <summary>
/// A utility class for dealing with COM events.
/// Needs error-handling and could potentially be refactored
/// into a regular class. Also, I haven't extensively tested this code;
/// there may be a memory leak somewhere due to the rather
/// low-level stuff going on in the class, but I think I covered everything.
/// </summary>
public static class ComEventUtils
{
/// <summary>
/// Get a list of all objects implementing an event sink interface T
/// that are listening for events on a specified COM object.
/// </summary>
/// <typeparam name="T">The event sink interface.</typeparam>
/// <param name="comObject">The COM object whose event sinks you want to retrieve.</param>
/// <returns>A List of objects that implement the given event sink interface and which
/// are actively listening for events from the specified COM object.</returns>
public static List<T> GetEventSinks<T>(object comObject)
{
List<T> sinks = new List<T>();
List<COM.IConnectionPoint> connectionPoints = GetConnectionPoints(comObject);
// Loop through the source object's connection points,
// find the objects that are listening for events at each connection point,
// and add the objects we are interested in to the list.
foreach(COM.IConnectionPoint connectionPoint in connectionPoints)
{
List<COM.CONNECTDATA> connections = GetConnectionData(connectionPoint);
foreach (COM.CONNECTDATA connection in connections)
{
object candidate = connection.pUnk;
// I tried to avoid relying on try/catch for this
// part, but candidate.GetType().GetInterfaces() kept
// returning an empty array.
try
{
sinks.Add((T)candidate);
}
catch { }
}
// Need to release the interface pointer in each CONNECTDATA instance
// because GetConnectionData implicitly AddRef's it.
foreach (COM.CONNECTDATA connection in connections)
{
Marshal.ReleaseComObject(connection.pUnk);
}
}
return sinks;
}
/// <summary>
/// Get all the event connection points for a given COM object.
/// </summary>
/// <param name="comObject">A COM object that raises events.</param>
/// <returns>A List of IConnectionPoint instances for the COM object.</returns>
private static List<COM.IConnectionPoint> GetConnectionPoints(object comObject)
{
COM.IConnectionPointContainer connectionPointContainer = (COM.IConnectionPointContainer)comObject;
COM.IEnumConnectionPoints enumConnectionPoints;
COM.IConnectionPoint[] oneConnectionPoint = new COM.IConnectionPoint[1];
List<COM.IConnectionPoint> connectionPoints = new List<COM.IConnectionPoint>();
connectionPointContainer.EnumConnectionPoints(out enumConnectionPoints);
enumConnectionPoints.Reset();
int fetchCount = 0;
SafeIntPtr pFetchCount = new SafeIntPtr();
do
{
if (0 != enumConnectionPoints.Next(1, oneConnectionPoint, pFetchCount.ToIntPtr()))
{
break;
}
fetchCount = pFetchCount.Value;
if (fetchCount > 0)
connectionPoints.Add(oneConnectionPoint[0]);
} while (fetchCount > 0);
pFetchCount.Dispose();
return connectionPoints;
}
/// <summary>
/// Returns a list of CONNECTDATA instances representing the current
/// event sink connections to the given IConnectionPoint.
/// </summary>
/// <param name="connectionPoint">The IConnectionPoint to return connection data for.</param>
/// <returns>A List of CONNECTDATA instances representing all the current event sink connections to the
/// given connection point.</returns>
private static List<COM.CONNECTDATA> GetConnectionData(COM.IConnectionPoint connectionPoint)
{
COM.IEnumConnections enumConnections;
COM.CONNECTDATA[] oneConnectData = new COM.CONNECTDATA[1];
List<COM.CONNECTDATA> connectDataObjects = new List<COM.CONNECTDATA>();
connectionPoint.EnumConnections(out enumConnections);
enumConnections.Reset();
int fetchCount = 0;
SafeIntPtr pFetchCount = new SafeIntPtr();
do
{
if (0 != enumConnections.Next(1, oneConnectData, pFetchCount.ToIntPtr()))
{
break;
}
fetchCount = pFetchCount.Value;
if (fetchCount > 0)
connectDataObjects.Add(oneConnectData[0]);
} while (fetchCount > 0);
pFetchCount.Dispose();
return connectDataObjects;
}
} //end class ComEventUtils
/// <summary>
/// A simple wrapper class around an IntPtr that
/// manages its own memory.
/// </summary>
public class SafeIntPtr : IDisposable
{
private bool _disposed = false;
private IntPtr _pInt = IntPtr.Zero;
/// <summary>
/// Allocates storage for an int and assigns it to this pointer.
/// The pointed-to value defaults to 0.
/// </summary>
public SafeIntPtr()
: this(0)
{
//
}
/// <summary>
/// Allocates storage for an int, assigns it to this pointer,
/// and initializes the pointed-to memory to known value.
/// <param name="value">The value this that this <tt>SafeIntPtr</tt> points to initially.</param>
/// </summary>
public SafeIntPtr(int value)
{
_pInt = Marshal.AllocHGlobal(sizeof(int));
this.Value = value;
}
/// <summary>
/// Gets or sets the value this pointer is pointing to.
/// </summary>
public int Value
{
get
{
if (_disposed)
throw new InvalidOperationException("This pointer has been disposed.");
return Marshal.ReadInt32(_pInt);
}
set
{
if (_disposed)
throw new InvalidOperationException("This pointer has been disposed.");
Marshal.WriteInt32(_pInt, Value);
}
}
/// <summary>
/// Returns an IntPtr representation of this SafeIntPtr.
/// </summary>
/// <returns></returns>
public IntPtr ToIntPtr()
{
return _pInt;
}
/// <summary>
/// Deallocates the memory for this pointer.
/// </summary>
public void Dispose()
{
if (!_disposed)
{
Marshal.FreeHGlobal(_pInt);
_disposed = true;
}
}
~SafeIntPtr()
{
if (!_disposed)
Dispose();
}
} //end class SafeIntPtr
} //end namespace YourNamespaceHere
In VB6 the event can only be raised from within the class (or Form as the case may be) declaring the Event. To force a event to be raised in VB6 you need to expose a method on the class to do this. If you don't have the source code the you are out of luck.
From the documentation
RaiseEvent eventname [(argumentlist)]
The required eventname is the name of
an event declared within the module
and follows Basic variable naming
conventions.
For example
Option Explicit
Private FText As String
Public Event OnChange(ByVal Text As String)
'This exposes the raising the event
Private Sub Change(ByVal Text As String)
RaiseEvent OnChange(Text)
End Sub
Public Property Get Text() As String
Text = FText
End Property
Public Property Let Text(ByVal Value As String)
FText = Value
Call Change(Value)
End Property
Sorry to be the bearer of bad news.

Categories