11-18-2015 10:50 PM
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).**
11-19-2015 04:00 PM
In LabVIEW you can't create raw delegates (what you are calling a "function pointer" is actually a little more complicated) but you can register for events and attach a callback VI to handle them. This means that, like you said, you cna't use a constructor that requires a delegate parameter.
What you can do is wrap the raw API and when the event is attached ("add" portion) then create the API instance using that value. That way you can use a Constructor node in LabVIEW and then immediately use a Register for Events which will allow your wrapper class to properly create the inner API object.
This API sounds very unusual and not compliant to Microsoft Design guidelines.
11-20-2015 12:03 AM
I did think it was unusual. Being very inexperienced in C#, what would such a wrapper look like? Do I need some sort of main() that calls the constructor of the PROPEngine? Does this all need to be public? How would the PROPEngine instance know to invoke my event, instead of the internal private event they have set up (which I don't have any info on)?
11-20-2015 04:50 AM
You shoot yourself in the foot.
Never use .NET within labview.
Program all in native labview, or go full visual studio with measurement studio.
11-20-2015 05:07 AM
@Alessandro__ wrote:
You shoot yourself in the foot.
Never use .NET within labview.
Program all in native labview, or go full visual studio with measurement studio.
I actually think this is pretty bad advice. .NET is an incredibly powerful library and provides a huge amount of utility classes that would save 1000's of hours of development to re-create. Some examples (which I have used recently) include: Tray Icon, Taskbar Progress Bars, PC Performance Monitoring, Process (for launching & monitoring other programs) and a library for sending files over SFTP.
Yes - there are some features missing for calling .NET code that can be tricky to work around (which you do by creating a wrapper which exposes the functions you need in a way you can call with LabVIEW) but to say 'never use .NET' is a bit extreme!
11-20-2015 06:19 AM
it doesn't matter if it's powerful.
Customer doesn't care if you program in assembly or Labview or C#.
Customer wants job done and well done.
.NET is poor integrated into labview, it's a pain in the neck.
If you have to mix .NET with labview there is something wrong.
Most of the time Labview is missing a functionality so people just "wraps win32" or "wraps .net" or "wraps ActiveX", and this is the worst thing to do.
NI is to blame for creating such a limited programming enviroinment. Plain and simple.
Almost 2016 still NO UNICODE STRINGS TYPE.
Well played NI! clap clap
It's the typical arrogant american move, they think ASCII is enough for an alphabet
11-20-2015 12:36 PM
Hey, nice soapbox.
I don't really have time to reprogram all ~800 some odd VIs that comprise my application. I've been given this API to interface with a sensor my client has contracted with. The rest of the API seems simple enough, it's just creating the PROPEngine and event handler that's giving me problems. All I want to know is how to create a wrapper that can make me an instance of this class. Once I have an instance of the PROPEngine, I have a set of invoke + property nodes ready to go that will integrate this sensor with the rest of my application.
If you guys have any docs on how to do this, I'd love to see them. Again, apologies for my inexperience with C# that I haven't been able to figure this out yet.
11-22-2015 12:20 PM
To give you the best answer - would it be possible to share the API and how you plan to use it? I think it will be quite possible to do what you suggest but I would like to point you in the right direction.
11-22-2015 12:47 PM
Unfortunately I'm under NDA and cannot share the API directly. I could describe the structure to you if that would help.
We have the PropEngine on the outermost layer of the API. This is what the application interfaces with. PropEngine has several public methods that you can invoke to describe valid "props" which are available to the PropEngine. You create "props" using a GUI based "PropConfiguration" class that you may invoke if you have a GUID for which you'd like to create the prop. Once you have a prop, each prop can have multiple datasources (created using methods on the prop). Each datasource can have several signal types, with each signal type having multiple channels of data, each channel representing a time when you sampled some data. Channels contain timestamps, along with the actual data. All of these tasks are easily accomplished using invoke nodes, property nodes, for loops etc.
So, using the props isn't all that difficult; the main issue is that creating an instance of the PropEngine requires passing it a delegate which points to your own event handler function. The events that the PropHandler can raise are private (so even decompiling the dll doesn't reveal much), and minimally documented. They do give a table with some event data, but not enough to make a proper event handler. The Event handler must be descended from the EventHandler class: https://msdn.microsoft.com/en-us/library/db0etb8x%28v=vs.110%29.aspx
Here's some example code they suggest you use, given you were writing your application in C#:
using PROP.PropEngine; private void PropEngineEventHandlerFunc(object sender, PropEngineEventArgs eventArgs) { ... } // Create the PropEngine _propEngine = new PropEngine(PropEngineEventHandlerFunc); ... // Dispose the PropEngine _PropEngine.Dispose();
I know this doesn't fully answer your question, but I hope it makes things a little clearer. I may be able to share the documentation of the API with you, but I'll have to check on that.
11-22-2015 12:52 PM
@ijustlovemath wrote:
Unfortunately I'm under NDA and cannot share the API directly. I could describe the structure to you if that would help.
We have the PropEngine on the outermost layer of the API. This is what the application interfaces with. PropEngine has several public methods that you can invoke to describe valid "props" which are available to the PropEngine. You create "props" using a GUI based "PropConfiguration" class that you may invoke if you have a GUID for which you'd like to create the prop. Once you have a prop, each prop can have multiple datasources (created using methods on the prop). Each datasource can have several signal types, with each signal type having multiple channels of data, each channel representing a time when you sampled some data. Channels contain timestamps, along with the actual data. All of these tasks are easily accomplished using invoke nodes, property nodes, for loops etc.
So, using the props isn't all that difficult; the main issue is that creating an instance of the PropEngine requires passing it a delegate which points to your own event handler function. The events that the PropHandler can raise are private (so even decompiling the dll doesn't reveal much), and minimally documented. They do give a table with some event data, but not enough to make a proper event handler. The Event handler must be descended from the EventHandler class: https://msdn.microsoft.com/en-us/library/db0etb8x%28v=vs.110%29.aspx
Here's some example code they suggest you use, given you were writing your application in C#:
using PROP.PropEngine; private void PropEngineEventHandlerFunc(object sender, PropEngineEventArgs eventArgs) { ... } // Create the PropEngine _propEngine = new PropEngine(PropEngineEventHandlerFunc); ... // Dispose the PropEngine _PropEngine.Dispose();I know this doesn't fully answer your question, but I hope it makes things a little clearer. I may be able to share the documentation of the API with you, but I'll have to check on that.
OK. Do you need to expose the "event" publically in LabVIEW? if so you could simply create an event on your wrapper and call the event in the PropEngineEventHandlerFunc private method. Then you could use a callback VI in LabVIEW easily enough.