Jump to content

Can someone help me with an example for an existing wrapper DLL which uses function pointer with callback function


Recommended Posts

Hello I hope someone can help me, I have been trying to get this to work for days but no success yet.

 

What I want to do

I got a dll with a wrapper, this dll and wrapper can be used for a special datalogger. The wrapper contains multiple functions, I have got some of them working using a CLFN, for instance I can read a jumper setting from the datalogger in LabView. So far everything is fine.

Of course I am not that interested in the jumper setting, I would rather use the datalogger to log data.

I need to run one function to start logging. When there is enough data in the databuffer of the driver dll it will call a callback function I need to write in Labview, here is the function which starts the logging as described in the header file:

 

Int LOGEXPORT __sdcall LOG_StartLogging{const char ** logFilename, ptLogCallbackfunction ptFunction, ptObject ptObject}

logFilename needs to be a NULL pointer, from my understanding I do not need to do anything with it. Only provide it. If I do not do anything with it some data is saved at a default location.

ptLogCallbackfunction, as far as I know I need to provide a pointer to the callback function

ptObject ptObject, this must be a void pointer

 

The header also provided how the callback function could look like in 😄

 

Void __stdcall callbackLog(ptObject object, const int ArraySize, const StructLogdata *const logDataArray)

{

// copy data from logDataArray for use in my code

}

 

StructLogdata is a struct with different datatypes.

 

What I did try

I did a search on internet there are some examples about callback functions, most of them point to NET and active X. They use a reg Event Callback. I did read the helpfile at the end of the helpfile there is the text:

“Use the Event structure or the Register For Events function to register and handle non-.NET or non-ActiveX events dynamically” I am having a hard time to find a proper example. I did find this site with some information:

https://knowledge.ni.com/KnowledgeArticleDetails?id=kA00Z000000P8XJSA0&l=nl-NL

and this one about the refnum:

https://forums.ni.com/t5/LabVIEW/Passing-Event-Refnum-to-Call-Library-Function/td-p/4107748

So I did make a user event, I wired a static Vi ref containing a callback vi to the input of this user event. I did wire the user event to a Reg event and wired the event registration refnum to the start logging dll call library function node function pointer input and also to an event structure. I did place a counter in the event structure. But nothing happens besides for an exception which makes Labview crash.

The parameters I did use in the call library function are:

logFilename: type numeric, datatype: Signed Pointer-sized int Pass: pointer to value

ptLogCallbackfunction: type: adapt to type, Data format handles by value (tried all options)

ptObject: same as logFilename

 

I did try a lot of different things. Writing them in this post will result in chaos and I don’t want that. I hope someone can help me with a simple example how to make this work, or point me into the correct direction.

Thank you all for the help!

Link to comment

LabVIEW cannot supply callbacks for unmanaged function calls. You will have to create a C/C++ wrapper DLL that creates and registers the callback with the function and inside that callback use PostLVUserEvent to send it to LabVIEW.

You've read those articles but haven't quite understood that you need a DLL wrapper and it is what actually registers and executes the callback with the function.

Edited by ShaunR
Link to comment

Well the header file I mention is from the wrapper someone else wrote. I have got some functions working from this wrapper. The only part I havent managed to get working is the part with the callback. So what you are saying I need a wrapper to use this wrapper because it uses a callback function? I will read the example you did provide thank you!

Link to comment
16 hours ago, Neon_Light said:

Well the header file I mention is from the wrapper someone else wrote. I have got some functions working from this wrapper. The only part I havent managed to get working is the part with the callback. So what you are saying I need a wrapper to use this wrapper because it uses a callback function? I will read the example you did provide thank you!

Do you have the source code of that wrapper? If so you can modify it, otherwise you will have to create a wrapper around this wrapper or around the original lower level API.

Or maybe that wrapper implements this callback functionality and the lower level API is simply a function API where the caller has to implement its own task handling. In that case it may be simpler to directly go to this lower level API and implement the parallel task doing the logging monitoring entirely in LabVIEW.

Edited by Rolf Kalbermatter
Link to comment
12 hours ago, Rolf Kalbermatter said:

Do you have the source code of that wrapper? If so you can modify it, otherwise you will have to create a wrapper around this wrapper or around the original lower level API.

Or maybe that wrapper implements this callback functionality and the lower level API is simply a function API where the caller has to implement its own task handling. In that case it may be simpler to directly go to this lower level API and implement the parallel task doing the logging monitoring entirely in LabVIEW.

Hello Rolf and ShaunR, Thank you for the help!

I do not have the source code of the wrapper but they included 2 files a Typedefs.h and a interface.h

The:

Int LOGEXPORT __sdcall LOG_StartLogging{const char ** logFilename, ptLogCallbackfunction ptFunction, ptObject ptObject}

is implemented in the dll and I can call it with a CLFN, first I tried to wire the output of a Register For Events function to the "ptLogCallbackfunction" as I understood this would provide a function-pointer to a Labview function/vi which would then behave as a callback function. It makes Labview crash though. When the driver is not running it returns a error message. I think the function does not try to use the callback pointer when the driver is not running.

My communication might not be completely correct as I am new to callback functions. Maybe this helps, above example text:

Void __stdcall callbackLog(ptObject object, const int ArraySize, const StructLogdata *const logDataArray)

there is also the text:

typedef Void( __stdcall *callbackLogFunction)(ptObject object, const int ArraySize, const StructLogdata *const logDataArray)

12 hours ago, Rolf Kalbermatter said:

In that case it may be simpler to directly go to this lower level API and implement the parallel task doing the logging monitoring entirely in LabVIEW.

I can give it a try, but how do I approach this?

I can also try to code it in C, think it is interesting and fun but it has been a while since I did code C. Although I planned to refresh my C knowledge, this would take time and focus on one thing to learn at a time might be more successful. It is late now, I will read trough the examples and Using External code pdf tomorrow. Thank you for the help you provided! 

 

Link to comment
1 hour ago, Neon_Light said:

this would provide a function-pointer to a Labview function/vi which would then behave as a callback function.

No. You cannot call LabVIEW VI's as function pointers in unmanaged code ... period!

I will reiterate:

On 1/15/2023 at 3:42 PM, ShaunR said:

You will have to create a C/C++ wrapper DLL that creates and registers the callback with the function and inside that callback use PostLVUserEvent to send it to LabVIEW.

It's only worth going to the original driver and interfacing directly with LabVIEW *IF* it means you don't need a callback.

Edited by ShaunR
Link to comment

Hello ShaunR, thank you for the answer. I have to admit it was not the answer I hoped for as this will take more time, on the other side learning new things is always a great thing 🙂 . I did read this forum:

 

And I think for me the most important points are:

Although the document is old I can use most parts of: "Using External Code in LabVIEW". The link to the Ni site contains more recent info: https://www.ni.com/docs/en-US/bundle/labview/page/lvhelp/labview_help.html

One part which is outdated of the external code document is how to set up the environment in the IDE. At this time I am using VSC, as a IDE. Do you have a link to a VSC setup to develop a DLL for Labview? 

 

 

Link to comment
10 minutes ago, Neon_Light said:

One part which is outdated of the external code document is how to set up the environment in the IDE. At this time I am using VSC, as a IDE. Do you have a link to a VSC setup to develop a DLL for Labview? 

The principle is still very much the same. The only thing that won't match are possible screen shots and maybe the naming is not always the same. DLL development was implemented with Windows 3.0 and has only changed VERY little over time. IDE settings may have changed somewhat more but it is not really feasible to make a new document for every new Visual Studio version. (And Visual Studio Code, and Intel C, and Bloodshed's DevC++, Watcom C++, Gnu C++, etc, etc).

Edited by Rolf Kalbermatter
Link to comment

Hello I  did give it a try I am able to compile some C code send some data to the created DLL and receive data from the DLL. So that is great 🙂 thanks !

The original DLL however will return a array with structs to my C DLL I will try to implement thePostLVUserEvent part there . When I try to make an even structure as an experiment I am not able to get the array out again. This makes me think I am not using the event method in the correct way. what will be the best way to get an array from the DLL back to the Labview event? Do I need to send every array element back with a PostLVUserEvent?

 

 

 

 

 

1178281110_Arraytoevent.PNG.28988f4090ea13fdea7c8a38a43e74f1.PNG

 

Link to comment
1 hour ago, Neon_Light said:

Hello I  did give it a try I am able to compile some C code send some data to the created DLL and receive data from the DLL. So that is great 🙂 thanks !

The original DLL however will return a array with structs to my C DLL I will try to implement thePostLVUserEvent part there . When I try to make an even structure as an experiment I am not able to get the array out again. This makes me think I am not using the event method in the correct way. what will be the best way to get an array from the DLL back to the Labview event? Do I need to send every array element back with a PostLVUserEvent?

1178281110_Arraytoevent.PNG.28988f4090ea13fdea7c8a38a43e74f1.PNG

 

There is no real problem to create such an array directly in the callback function, to be sent through the user event. But you must understand that the actual array must be a LabVIEW managed array handle. 

Basically something like this will be needed (and you will need to adjust the typedef of the cluster to match what you used in your LabVIEW code, since you "forgot" to attach your code):

#include "lv_prolog.h"

typedef struct {
    int32_t status;
    double value;
} DataRecord;

typedef struct
    int32_t size;
    DataRecord elm[1];
} DataArrayRec, *DataArrayPtr, **DataArrayHdl;

#include "lv_epilog.h"

/* if the callback can be reentrant, meaning it can be called while another callback is already executing,
use of a global variable for the data array is NOT safe and needs to be handled differently!!!! */

static DataArrayHdl handle = NULL;

void yourCallback(.......)
{
    int32_t i, size = somewhereFromTheParameters;
    MgErr err;

    if (!handle)
    {
        handle = DSNewHandle(sizeof(DataArrayRec) * (size - 1));
        if (!handle)
            err = mFullErr;
    }
    else
    {
        err = DSSetHandleSize(handle, sizeof(DataArrayRec) * (size - 1));
    }
    if (!err)
    {
        (*handle)->size = size;
        for (i = 0; i < size; i++)
        {
            (*handle)->elm[i].status = status[i];
            (*handle)->elm[i].value = values[i];
        }
        err = PostLVUserEvent(lvEventRefnum, &handle);
    }
}

The easiest way to actually get the datatype declaration for the array that you need to pass to PostLVUserEvent() would be to create a dummy Call Library Node with a parameter configured as Adapt to Type, and then right click on the node and select "Create C Souce code". After choosing where to write the file it will create a C source file that contains the necessary data type declarations.

Edited by Rolf Kalbermatter
Link to comment

Thank you Rolf !! 

but the thing I can still not find is how do I get acces to the array in the event structure. If I wire a number for example index to the create user event it ends up in the event structure. Wile the callback array does not appear. I added a screenshot, hope that helps. 

No Array .PNG

Link to comment
10 minutes ago, Neon_Light said:

Thank you Rolf !! 

but the thing I can still not find is how do I get acces to the array in the event structure. If I wire a number for example index to the create user event it ends up in the event structure. Wile the callback array does not appear. I added a screenshot, hope that helps. 

No Array .PNG

It's unclear since you still don't want to post VIs but only images so we have to guess. But I would say the array simply has no label. The node inside the event structure only lets you select named elements. The "CallBack" word looks like it may be a label, but it is probably just a free label like the "Test" in the image below. 

Edited by Rolf Kalbermatter
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.