Executing LabVIEW VIs through C style function pointers via .NET

Problems exist to be solved.


Some problems are so complex that you cannot solve them when you need to. With other problems it is not a matter of complexity, but a matter of not having all the pieces of the puzzle.


Some problems are so interesting that it pays to keep them in the back of your head, just in case you ever find that missing piece that allows you to solve them.


One of those problems is that with the current LabVIEW external library interface it is impossible to execute LabVIEW VIs in a DLL through a C style function pointer.


This feature does not exist, even though lots of programmers would really benefit from it.


2 weeks ago I was active on the Visual C++ newsgroup as usual, when learned –by pure coincidence- about an advanced function in the .NET framework called Marshal.GetFunctionPointerForDelegate.


I immediately realized that this was the key to solving the old C style function pointer issue


Problem description


Suppose that we have a DLL that exports a function. This function takes a function pointer as an argument, to be executed sometime later to pass some data to the calling application.


The Test DLL code


The idea is to test the ability to run a LabVIEW VI through a function pointer. To test this we need a DLL which will test this for us.


The following very simple test function has been created for this purpose.


This is the expected prototype of the callback function


typedef void (__stdcall *fSimpleCallBack)(int i);


And this is the DLL function that will perform our test:


//Test of the simple callback


void Simplefoo(int i, fSimpleCallBack cb)


{


  (cb)(i);  //invoke the callback function.


}


As you can see, the LabVIEW VI will be executed with the supplied parameter through the supplied function pointer.


Principal solution


The very first action has to be creating a .NET delegate of which the signature matches the signature of the CallBack function that is required.


Then a proxy class is created with only 2 members: an event that is based on the new delegate function, and a method called ‘GetDelegate’.


LabVIEW has the ability to link a LabVIEW VI with a .NET event. The VI prototype is generated automatically, and matches the event delegate signature.


After registering a VI for the specified event, LabVIEW uses the GetDelegate method of our proxy class to extract the registered delegate from the event. That delegate is then converted to a function pointer via the ‘GetFunctionPointerForDelegate’ method of the Marshal class.


That function pointer can then be supplied to a native function that can execute the LabVIEW VI directly through the function pointer without requiring any modification at all.


A note on GetFunctionPointerForDelegate


The documentation for this method is sparse. It seems that VS2005 and .NET2.0 were rushed out of the door without taking the time to fully document the more advanced functions and providing code samples in the documentation.


Because of this, it is easy to miss that GetFunctionPointerForDelegate returns a pointer that uses the __stdcall calling convention by default.


It is also easy to overlook the fact that you can change this.


If you need a function pointer that uses another calling convention – e.g. __cdecl – then you can achieve this by applying the UnmanagedFunctionPointerAttribute attribute to the delegate definition.


The .NET code


namespace FunctionPointerProxy


{


  public class SimpleProxy


  {


    //Marshal a simple integer parameter as 32 bit integer.


    public delegate void SimpleProxyDelegate(


      [MarshalAs(UnmanagedType.I4)] int i);


 


    //export an event because that is the only way we are going to get


    //LabVIEW to give use a delegate.


    public event SimpleProxyDelegate SimpleEvent;


 


    //extract the delegate from the event. If there was no event


    //registered, this will trigger an exception which is caught by the


    //LabVIEW .NET interface node. No need to worry about it here.


    public Delegate GetDelegate()


    {


      return SimpleEvent.GetInvocationList()[0];


    }


  }


}


This is all the additional code you really need to make it work. The delegate function has to mirror the native callback prototype for 100%. To achieve this you have to use the MarshalAs attribute on each delegate parameter to insure correct behavior.


The event simply has to exist because LabVIEW has only 1 way to produce a delegate for a VI, and that is by registering a VI to an event.


And even then, the delegate is never directly accessible, so we have to create a ‘GetDelegate’ method that extracts the registered delegate, and returns it to LabVIEW.


As far as the .NET framework is concerned, we could simply make the event and the GetDelegate method static to remove the need to instantiate an object that we won’t need for anything else. But there are 2 reasons why we don’t do that:


  • LabVIEW does not support registering VIs to static events.
  • Our GetDelegate function would need way to know which delegate you want to get, in case there are multiple instances of callback VIs. Since the event is tied to an instance, it is easy enough to keep track of the events in LabVIEW.

The LabVIEW code


LabVIEW code is graphical, so I have chosen to mark the different steps in the block diagram below, and then add an explanation of each step in a numbered list.


 


  1. Instantiate a new SimpleProxy Class. We only need it to access the event. Sadly, LabVIEW does not yet support static events.
  2. Register a VI for the Event. The way to do this is to wire the SimpleProxy refnum to the event source and select the appropriate event. Right-click ‘VI Ref’ and select ‘Create Callback VI’. This will automatically generate a VI with the correct input and output parameters for you. This will also automatically wire a static VI reference to the VI Ref input. User parameter can be any LabVIEW type at all that you want to pass to the VI when the .NET event is triggered. We don’t need it, but if we did we would have to wire it before we generate the callback VI because it changes the VI prototype.
  3. Now that the event is registered, we can simply get the .NET delegate that represents the VI callback out of it.
  4. Use a static member of the Marshal class to get a funtion pointer to the delegate. This is where the magic happens. A native stub is generated for the delegate at runtime. The unmanaged parameter list is built according to the Marshalling attributes that were attached to the delegate signature.
  5. Cast the Delegate IntPtr to an Int32. Pointers are 32 bit on the x386 platform. We need this 32 bit value to supply it to the DLL function call that expects a 32 bit function pointer. Note that this function pointer uses the standard calling convention by default (see below for more info).
  6. We no longer need the FunctionPointer IntPtr. As long as the delegate itself is still in memory, the native stub will be valid.
  7. Execute the DLL function. This function will use the supplied callback function pointer to execute the callback VI. It will pass the supplied int parameter to the callback vi at the time of the function call.
  8. Unregister the Callback VI from the event, and close the various references that were opened.

Running the test


As is sometimes said: the proof is in the pudding.


To verify that everything works as advertised, I have put a simple dialog box in the callback VI that displays the value of the integer parameter.


Simply run the example and you will see the following dialog box:



Advanced implementation


The principal solution works, but is not yet ideal. Most importantly, there is too much clutter on the LabVIEW diagram for this solution to be aesthetically pleasing.


There are too many manual actions that have to be repeated each time you implement a callback function. I also wanted to show you an example of a callback function that is a bit more complex.


Test DLL


Consider the following callback prototype that is a bit more complex than the previous one:


typedef void (__stdcall *fAdvancedCallBack)(


                  int i,


                  char* aString,


                  wchar_t* wString);


Here we need to supply an integer, an ASCII string and a UNICODE string. The function exported from the DLL has the following implementation:


void Advancedfoo(int i, char* aString, fAdvancedCallBack cb)


{


  BSTR uString;


  int aLength = lstrlenA(aString);


  int uLength = ::MultiByteToWideChar(


                      CP_ACP, 0, aString, aLength, 0, 0);


  if(0 < uLength)


  {


    uString = SysAllocStringByteLen(0, uLength);


    ::MultiByteToWideChar(


                      CP_ACP, 0, aString, aLength, uString, uLength);


  }


  else


    uString = L“”;


 


   //invoke the callback function through its function pointer.


  (cb)(i, aString, uString);


}


LabVIEW itself does not support UNICODE strings, so we manually transform the supplied ASCII string to UNICODE. Apart from that the function body only contains a function call via the supplied callback pointer.


 .NET code


  [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]


  public class AdvancedProxy : IProxy


  {


    public delegate int AdvancedProxyDelegate(


      //A simple I4 parameter. Nothing special


      [MarshalAs(UnmanagedType.I4)] int i,


      //An ASCII string. this will be marshalled as UNICODE on the


      //.NET side and ASCII on the unmanaged side.


      [MarshalAs(UnmanagedType.LPStr)] string aString,


      //A UNICODE string. This will be marshalled as UNICODE on the


      //.NET side, and UNICODE on the unmanaged side.


      [MarshalAs(UnmanagedType.LPWStr)] string wString);


   


    public event AdvancedProxyDelegate AdvancedEvent;


 


    public Delegate GetDelegate()


    {


      return AdvancedEvent.GetInvocationList()[0];


    }


  }


The meaning of the different parts is the same as with the SimpleProxy example. The only real difference – apart from the delegate signature of course – is that all classes that are used as a proxy between a LabVIEW VI and a function pointer should implement the interface IProxy.


This interface has the following definition:


  public interface IProxy


  {


    Delegate GetDelegate();


  }


This allows us to make the LabVIEW code for getting a function pointer generic, instead of tied to the type of the callback delegate.


LabVIEW code



As you can see, the LabVIEW code is vastly simplified.


  1. The only thing that we cannot make generic is the event registration, because the event and as a result the VI reference are strongly typed.
  2. The process of retrieving the function pointer is generic because all proxy classes now implement the IProxy class for retrieving the event delegate.
  3. The call to the test function in the DLL now also takes a string parameter that gets expanded to UNICODE inside the test function to supply the callback function with the correct parameters.
  4. Cleanup of the event and references is also generic.

There are 2 new VIs. These are generic, so you can reuse them without limitation. This also makes it easy to support an asynchronous callback mechanism. You can register the event and supply the function pointer to the DLL.


The DLL stores it inside, and uses it any time it needs to. Meanwhile, your LabVIEW program can continue processing. You can keep the references cluster around – for example in a buffer – until such time as you want to disable the callback mechanism again.


Running the demo code


The Visual studio solution is for VS2005, and can be built with all versions of VS2005.


The LabVIEW project uses version 8.2. You need at least version 8.0 to have decent .NET support, and I used 8.2 because that is the latest version at this moment.


To run the demo, simply extract the sources and open and build the Visual studio solution. Then open the LV project and run the sample of your liking.


Conclusion


This article has outlined a relatively simple way to directly execute a LabVIEW VI through a C style function pointer without having to write C or C++ code. The demo code for this article is available under the MIT license as usual.


You still need to provide a .NET class library, but the code involved is so minimal and trivial that it should not stop anyone.


This approach also makes it possible to use the wealth of win32 API functions that require a callback function without having to resort to writing C or C++ code.


In the time it took me to write this article (which took longer than writing the code, as usual) I found 3 other ways to achieve the same result.


One is an ugly kludge that only fakes the callback functionality. The other performs a true callback to a LabVIEW VI, but it is even uglier. The third one is fairly elegant but has some other limiting factors.


I will explain those 3 lesser solutions in another article. Despite the fact that only one of them is useful; the principle behind all 3 of them might be of use in another situation.


Finally, the whole LabVIEW and function pointer issue has existed for a long time among LabVIEW programmers. I have done a bit of research, and as far as I can tell, I am the first to ever solve this problem without requiring C or C++ code.


So here and now I claim to be the first to implement and publicize a working C style function pointer callback mechanism in LabVIEW!


In my next article I will follow up with an example involving structures (Clusters in LabVIEW) and n dimensional arrays.

11 thoughts on “Executing LabVIEW VIs through C style function pointers via .NET”

  1. Hello!

    Your solution is usefull also to solve an old problem here in my lab. I’m new in C#. I have a problem with your sourcecode. My compiler (Borland Dev.Studio) can’t load the:
    using System.Collections.Generic;

    Any ideas?
    regards, Damjan

    KRIZMANCIC at TASC dot INFM dot it

  2. I am getting the error…
    VI version is later than the current LabVIEW version. An error occurred loading VI ‘CleanupCallback.vi’. LabVIEW load error code 9: VI version (8.2) is newer than the LabVIEW version (8.2b23).
    Strange….its basically the same version. Just off by a little b23. You would think a newer version would be somewhat backwards compatable. Guess not. So, how can I find out what is in “get callback function pointer” and cleanup callback? Great article..can’t wait to impliement it.

  3. Hello Joe,

    How did you update labview? I mean 8.2 is the latest version. I am getting the same error you did (for another vi though).

    George
    (mail: gpouikli at ee.duth.gr)

  4. Hi

    Thanks very much for this link, but I have a question when the callback function is a VI code and the caller is a dll function. Could you please read this post and give me a reply to”paulbin@126.com”?
    Thanks very much again.

  5. I’m new in .net, I have the similar problem, I need your help.

    The following is the prototype of the callback function:

    VOID(_stdcall *VideoFilter)(VOID *pcontext, BYTE *pData,ULONG dataLength)

    Can you give me the corresponding .NET code?

    Thank you in advance.

  6. Hi,

    i am trying to implement a hardware in labview for my master thesis (sports science). as i am no programmer i am having not enough knowledge to get the things working right.

    the problem i have got is simmilar to the one you solved.

    i need to suply a function with a pointer to a callback function defined as
    typedef BOOL (_stdcall *CHANNEL_EVENT_FUNC)(UCHAR ucANTChannel, UCHAR ucEvent);

    it is used in

    _declspec(dllexport) void ANT_AssignChannelEventFunction(UCHAR ucANTChannel, // Initialize Channel pointers
    CHANNEL_EVENT_FUNC pfChannelEvent,
    UCHAR *pucRxBuffer);
    used in the c file as following

    __declspec (dllexport) void ANT_AssignChannelEventFunction(UCHAR ucLink, CHANNEL_EVENT_FUNC pfLinkEvent, UCHAR *pucRxBuffer)
    {
    sLink[ucLink].pfLinkEvent = pfLinkEvent;
    sLink[ucLink].pucRxBuffer = pucRxBuffer;
    }

    if you could help me by solving the problem i would be very thankfull or if you could make things work. i would pay for it too.

    thank you a lot
    martin

    (email martin.boecskoer(at)aon.at)

  7. i need to pass the below structure from labVIEW to C DLL Can any one help me out …..is thr an option to pass without writting a wrapper?PL do help …..

    typedef struct

    {
    unsigned long NumOfParams;
    SCONFIG *ConfigPtr;
    } SCONFIG_LIST

    typedef struct
    {
    unsigned long Parameter;

    unsigned long Value;

    } SCONFIG

    pl do help me….

  8. i need to pass the below structure from labVIEW to C DLL Can you help me out in this….is there an option to pass without writting a wrapper?or could how could you pass these kinda structures in labVIEW.

    typedef struct

    {
    unsigned long NumOfParams;
    SCONFIG *ConfigPtr;
    } SCONFIG_LIST

    typedef struct
    {
    unsigned long Parameter;

    unsigned long Value;

    } SCONFIG

    kindly help me

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>