LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

getting a struct as a return from dll

I'm having a problem with a dll call I'm trying to use.

The dll has one input param: 'lpparm' which is a pointer to a structure which contains 3 arrays of bytes.
I have created a cluster with 3 clusters inside of it to pass the data to the dll, using "adapt to type" as the type - per an example I found in the forums.

The problem I am having is the dll runs, does what I expect and then labview crashes, with the message: "LabVIEW Development System has encountered a problem and needs to close. We are sorry for the inconvenience."

My hunch is it crashes because I don't know how to handle a struct output from the dll. For now I simply set the return type as numeric because I don't know what to do. The only options in LabVIEW for the return type are void, numeric, and string. How can a dll return a struct if it doesn't have "adapt to type" as a return type, as the inputs do?

Is there a way to handle this without having to change the dll (I don't have the source code)? Any help would be appreciated!

Regards,
Brad
LV 6.0.2
Message 1 of 8
(4,384 Views)


@BCK wrote:
I'm having a problem with a dll call I'm trying to use.

The dll has one input param: 'lpparm' which is a pointer to a structure which contains 3 arrays of bytes.
I have created a cluster with 3 clusters inside of it to pass the data to the dll, using "adapt to type" as the type - per an example I found in the forums.

The problem I am having is the dll runs, does what I expect and then labview crashes, with the message: "LabVIEW Development System has encountered a problem and needs to close. We are sorry for the inconvenience."

My hunch is it crashes because I don't know how to handle a struct output from the dll. For now I simply set the return type as numeric because I don't know what to do. The only options in LabVIEW for the return type are void, numeric, and string. How can a dll return a struct if it doesn't have "adapt to type" as a return type, as the inputs do?

Is there a way to handle this without having to change the dll (I don't have the source code)? Any help would be appreciated!

Regards,
Brad
LV 6.0.2




Without seeing the header file for the DLL this is just a wild guess. But your structure is a very good candidate for a headache even from a C programmers point of view. First what you describe is something which definitely won't work as expected. A LabVIEW array is handle or a pointer to a pointer. This is in 99.9% of the cases NOT what a DLL expects except if it was specifically programmed to be accessible from LabVIEW.

So remains the question, what your structure really looks like. The way you describe it would be something like this:

struct {
char *array1;
char *array2;
char *array3;
} MyStruct;

This would be a very bad parameter interface to a function. The documentation would have to tell you if the caller is supposed to allocate the necessary pointers and with what size or if the function will allocate them and with what method you would later have to deallocate them. Doing here not EXACTLY what the DLL programmer has implement will either create memory leaks or fatally crash. This type of structure would only be feasable accessible from within LabVIEW by creating an intermediate translation DLL.


More likely your structure will however look like this:

struct {
char array1[LENGTH1];
char array2[LENGTH2];
char array3[LENGTH3];
} MyStruct;

This is inlined by the C compiler to one single memory area. The total length of that memory block is
LENGTH1 * sizeof(array1[0]) + LENGTH2 * sizeof(array2[0]) + LENGTH3 * sizeof(array3[0])

Once you know that size you can allocate an array of uInt8 of that length and pass it as an Array, 1 dimenstion, C pointer, type 8 bit unsigned integer. After retun of the DLL call you will have to copy out the interesting information from that array.

Rolf Kalbermatter
Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 2 of 8
(4,377 Views)
Rolf,

Thank you for the reply - I appreciate your help. You are correct with your second example, I am sending 3 arrays of known length:

// Device Initialization parameters (ARFDeviceInit)
typedef struct ARFInitPrm_t
{
BYTE billLengthLongThr[8]; // Bill length long thresholds, mm (cassettes 1-8)
BYTE billLengthShortThr[8]; // Bill length short thresholds, mm (cassettes 1-8)
BYTE billThickness[8]; // Bill Thicknesses information (cassettes 1-8)
} ARFInitPrm_t;

I am still a little confused though. I passed the array into the dll as 1 dimension, 8 bit unsigned integer, and it still crashes - does the output of the dll write to that array, so I can connect an indicator direcly across from the input array?

The output of the dll is 'lpreply' that is a pointer to another structure of bytes and arrays of known length. Do I set the return type parameter = lpreply and what data type? Do I ingore the reply type completely?

Thanks again,
Brad Kettler
0 Kudos
Message 3 of 8
(4,363 Views)


@BCK wrote:
Rolf,

Thank you for the reply - I appreciate your help. You are correct with your second example, I am sending 3 arrays of known length:

// Device Initialization parameters (ARFDeviceInit)
typedef struct ARFInitPrm_t
{
BYTE billLengthLongThr[8]; // Bill length long thresholds, mm (cassettes 1-8)
BYTE billLengthShortThr[8]; // Bill length short thresholds, mm (cassettes 1-8)
BYTE billThickness[8]; // Bill Thicknesses information (cassettes 1-8)
} ARFInitPrm_t;

I am still a little confused though. I passed the array into the dll as 1 dimension, 8 bit unsigned integer, and it still crashes - does the output of the dll write to that array, so I can connect an indicator direcly across from the input array?

The output of the dll is 'lpreply' that is a pointer to another structure of bytes and arrays of known length. Do I set the return type parameter = lpreply and what data type? Do I ingore the reply type completely?

Thanks again,
Brad Kettler




You need to allocate a long enough array for the DLL to scribble into. You would do that with the Initialize Array function wiring up an u8 as type and a number 24 to the size.

Rolf Kalbermatter
Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 4 of 8
(4,359 Views)
Rolf,

I'm not quite getting it yet. I have attached a .vi that shows how I've implemented the dll and a screenshot of the dll manual - if you have time maybe you could take a look at it and let me know what I'm missing?

I initialized the output array to the size of the output I'm expecting, and wired that array indicator to the dll across from the input array, but it still crashes.

Thanks again,
Brad Kettler
0 Kudos
Message 5 of 8
(4,353 Views)


@BCK wrote:
Rolf,

I'm not quite getting it yet. I have attached a .vi that shows how I've implemented the dll and a screenshot of the dll manual - if you have time maybe you could take a look at it and let me know what I'm missing?

I initialized the output array to the size of the output I'm expecting, and wired that array indicator to the dll across from the input array, but it still crashes.

Thanks again,
Brad Kettler




Forget Frame zero of the sequence. It does nothing useful as what I meant with the previous message you already did by creating a cluster, typecasting it to a string and passing it to your DLL call. However the real problem is that your DLL takes two parameters. The first is the ARFInitPrm_t which is implemented already in your VI. But there is a second parameter ARFDevInitRepl_t which you must also configure in the Call Library Node. By not doing so you mess up the stack and a hard crash is absolutely unavoidable.

The problem I see with this structure is however the first Field "Common". It says that this is a pointer to another structure and this is something you really can't do easily in LabVIEW. I would rather expect this to be inlined as well, extending the size of the entire structure by 81 bytes to 98 bytes. If it is really a pointer in there you have a real problem. A wrapper DLL to translate between this DLL and LabVIEW would be the most easy solution.

Rolf Kalbermatter
Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 6 of 8
(4,337 Views)
Rolf,

Thanks again for the help. Unfortunately, I believe the 'common' parameter actually is a pointer within the pointer, I didn't have any luck getting the dll to work.

I have been concurrently working on a direct serial interface as well, and was able to get the hardware working without the dll. Either way I learned a lot from your help - and hopefully others will too.

Regards,
Brad
0 Kudos
Message 7 of 8
(4,317 Views)


@BCK wrote:
Rolf,

Thanks again for the help. Unfortunately, I believe the 'common' parameter actually is a pointer within the pointer, I didn't have any luck getting the dll to work.

I have been concurrently working on a direct serial interface as well, and was able to get the hardware working without the dll. Either way I learned a lot from your help - and hopefully others will too.

Regards,
Brad




Well, if you have the protocol description to the device, this is actually most often the easiest way to go. LabVIEW instrument communication is quite easy and there is almost no way to crash the system by going that route, so you have time to tinker and try how things should work properly without having to get pointers and memory areas right first.

I usually prefer direct communication to external devices above any 3rd party DLL if that device is connected through a standard communication interface such as serial, GPIB, TCP/IP or such.

Rolf Kalbermatter
Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
0 Kudos
Message 8 of 8
(4,310 Views)