Search code examples
c#event-handlinglabview

Event handling in LabVIEW via C#- Constructor Node on type PROP.PropEngine not found


I'm building an application in LabVIEW that requires me to register .NET events via a callback. The API I've been been given doesn't have public events in it, so I can't register them properly. Also, the constructor I'm trying to create isn't working. I'm very new to event handling and C# in general, so I apologise if anything is missing from my description.

Using an API provided by a company I'm working with, I made this event handler:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using PROP.PropEngine;
namespace IMTEventHandler
{
    public delegate void PropEngineEventHandlerDelegate(object sender, PROP.PropEngine.PropEngineEventArgs eventArgs);
    public class IMTDelegate
    {
        public static event PROP.PropEngine.EventHandler PropEvent
        {
            add { inner.PropEvent += value; }
            remove { inner.PropEvent -= value; }
        }
        public static void PropEngineEventHandlerFunc(object sender, PROP.PropEngine.PropEngineEventArgs eventArgs)
        {
            return ;
        }
        public PropEngineEventHandlerDelegate propEventHandler = PropEngineEventHandlerFunc;
        //Pointer to our Event Handler
    }
}

Their documentation says that the constructor takes a function pointer to the event handler, eg:

_propEngine = new PropEngine(propEventHandlerFunc)

I don't have access to the original code though, so I thought I should instead create my own class, with the function pointer being a property/member of the class:

public PropEngineEventHandlerDelegate propEventHandler = PropEngineEventHandlerFunc;

I think that I could just write this as a private function without a new class, but the end application won't allow for that, unfortunately.

In the application, I got some class conflicts when trying to pass the constructor this function pointer, but I just figured I had to do a type cast to make them work (rookie mistake). Now I get this constructor node error, and I think it's because the pointer I'm sending is a bad pointer.

The documentation indicates that the event handler should take two arguments:

void PropEngineEventHandlerFunc(object sender, PROP.PropEngine.PropEngineEventArgs eventArgs)

sender is an object, and eventArgs is a 'struct' of sorts that contains all of the event data.

Problems: My function pointer is bad but I don't know why. Also, the event types aren't exposed, so I can't account for them (I think).


Solution

  • There are two main obstacles in your current approach:

    • LabVIEW cannot create delegates, so it cannot construct a PropEngine since (according to you) its constructor requires a PropEngine.EventHandler delegate.
    • By using a static event and event handler, your LabVIEW program is limited to a single PropEngine instance.

    Your idea of creating an adapter will enable you to use PropEngine class, just refactor it a little.

    C# Adapter

    I've written two classes in a Class Library (.dll):

    • MockProp -- which is a guess at your API
    • MockPropAdapter -- which allows LabVIEW to use the MockProp class

    The adapter exposes a public event that LabVIEW can subscribe to with the Register Event Callback node. Your LabVIEW callback VI will be called when the PropEngine event is raised as long as your LabVIEW program creates an adapter and subscribes to its event before it uses the adapter to create a PropEngine instance.

    using System;
    
    namespace MockProp
    {
       // This is my sketch of the API you've been given.
        public class MockProp
        {
           // I'm assuming that the API publishes the event handler's signature.
           public delegate void MockPropEventHandler(object sender, EventArgs args);
    
           // I'm assuming that the API's event is instance-based and not static.
           private event MockPropEventHandler MockPropEvent;
    
           // I'm assuming that it's keeping a reference to your event handler.
           private MockPropEventHandler clientHandler;
    
           public MockProp(MockPropEventHandler eventHandler)
           {
              this.clientHandler = eventHandler;
    
              // I'm assuming that it also auto-subscribes to the event because it is a constructor parameter.
              this.MockPropEvent += this.clientHandler;
           }
    
           // Provide an easy way for LabVIEW to trigger the event for demonstration purposes.
           public void NotifyEvent()
           {
              if (this.MockPropEvent != null)
              {
                 var args = new EventArgs();
                 this.MockPropEvent(sender: this, args: args);
              }
           }
    
           // Allow the object to be garbage collected by removing all retaining references.
           public void Release()
           {
              this.MockPropEvent -= this.clientHandler;
              this.clientHandler = null;
           }
        }
    
       // Here's one way to allow LabVIEW to subscribe to and handle private events
       public class MockPropAdapter
       {
          private MockProp mockProp;
    
          public MockPropAdapter()
          {
             // NOOP
          }
    
          // LabVIEW can subscribe to this event.
          public event MockProp.MockPropEventHandler MockPropEventRepeater;
    
          public MockProp CreateMockProp()
          {
             if (this.MockPropEventRepeater == null)
             {
                throw new InvalidOperationException(message:
                   "Subscribe to MockPropEventRepeater first. Otherwise, the Prop's event cannot be repeated.");
             }
             else
             {
                this.mockProp = new MockProp(eventHandler: this.MockPropEventRepeater);
                return this.mockProp;
             }
          }
    
          private void RepeatMockPropEvent(object sender, EventArgs args)
          {
             if (this.MockPropEventRepeater != null)
             {
                this.MockPropEventRepeater(sender, args);
             }
          }
       }
    }
    

    LabVIEW Client

    In the block diagram snippet below, the LabVIEW program:

    1. Creates an adapter
    2. Registers for the repeater event
    3. Uses the adapter to create a MockProp instance
    4. Uses the MockProp instance to raise its event
    5. This is when the handler VI handles the event
    6. Unsubscribes from the repeater event
    7. Releases the MockProp instance
    8. Releases the adapter

    enter image description here