LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Returning variably-sized array of strings from DLL to LabVIEW

I have a DLL that returns an array of strings.

When I generate test data in the DLL and send it back, all is well. The array indicator shows the right amount of strings and all the data is correct.

The trouble is, in the real case, I don't know AHEAD OF TIME how many strings to return. The DLL is browsing an OPC server asking for channel tags, and there is no way (that I know of) to find out how many there are. I keep asking the browser interface for a batch of 20, and whenever it comes back with less than 20, I know I'm done. There could be 1 or a hundred, I don't know until I'm done.

So I can't resize the output array (of string handles) with the right number until I've already been thru th
e process.

Also, I cannot use SetCINArrayResize, only NumericArrayResize. (it's not available - I don't know why - I'm in a C++ Multithreaded DLL)

So what I'm doing now is at the beginning, resizing the array to 1000 (assuming there will never be more than that). I then read a batch of strings, convert them to ASCII (from UNICODE), create a new LSTRHandle (with NumericArrayResize - it creates the handle for me), and poke that handle into the master array. I then resize the master array AGAIN, setting the real size, since I counted how many I stored.

I get a crash occasionally, not every time. It's usually when I close the calling VI.

My question is, does the display (of the string array) get updated during my process? If I set the array size to 1000 elements; does it care that some of those elements are garbage? I'm not setting the "dimSize" value until after I've resized it down.

Any other things I might be overlooking?
Steve Bird
Culverson Software - Elegant software that is a pleasure to use.
Culverson.com


LinkedIn

Blog for (mostly LabVIEW) programmers: Tips And Tricks

0 Kudos
Message 1 of 9
(5,838 Views)
CoastalMaineBird;

Use the function DSSetHandleSize() to resize the array of strings. You can then input an empty array of strings from LabVIEW. After knowing the number of channel tags, you can resize the array of strings:

// Assuming: Array_Str_Hdl AllChannels
// where Array_Str_Hdl is an array of handles
// to LV strings

DSSetHandleSize(AllChannels, sizeof(int32) + num_of_channels * sizeof(LStrHandle) );

// Then "manually"

(*AllChannels) -> dimSize = num_of_channels;


Regards;
Enrique
www.vartortech.com
0 Kudos
Message 2 of 9
(5,838 Views)
> I have a DLL that returns an array of strings.
>

Since you seem to be writing the DLL to return the data, you might
consider doing what NI often does. Call into the DLL to ask how many
items will be returned. Sometimes this is the same call with parameters
set to leave the array alone and other times it is a unique call. When
you know the number, you can set the array size on the diagram using
Reshape Array. You could also presize the strings if you like.

From what you described, I suspect the problem is related to your use
of NumericArrayResize instead of the generic SetCINArraySize. Without
seeing the code, it is difficult to say exactly, but numeric arrays are
flat and don't need to worry about deallocating strings. This would
normally c
ause a leak rather than a corruption, but regardless, you
might try again using SetCINArraySize. Look at extcode.h for the exact
spelling.

Greg McKaskle
0 Kudos
Message 3 of 9
(5,837 Views)
But the problem is that I don't know how many items I have until I get them all. The strings come from an OPC server, and I cannot ask how many there are ahead of time. I could transfer them all once, just to count them, then set the array size, then transfer them all again, storing them this time. But that's wasteful.

The SetCINArraySize function is simply not available. I get LNK2001 error - unresolved external symbol _SetCINArraySize. I see it in the EXTCODE.h file plain as day but look at the difference:

TH_REENTRANT MgErr NumericArrayResize(int32, int32 UHandle*, int32);
MgErr SetCINArraySize(UHandle, int32, int32);

I don't know what exactly the TH_REENTRANT does, but I get a link error. That's why I use NumericArrayResize() ins
tead.
Steve Bird
Culverson Software - Elegant software that is a pleasure to use.
Culverson.com


LinkedIn

Blog for (mostly LabVIEW) programmers: Tips And Tricks

0 Kudos
Message 4 of 9
(5,837 Views)
I understand what you're saying, but I don't understand why that's any different than NumericArrayResize.

Also, I'm trying to avoid storing ALL the tags within the DLL until I know how many, then moving them all into the LV array. I want to receive a batch, convert them to ASCII, put them into the LV array and repeat until done.
Steve Bird
Culverson Software - Elegant software that is a pleasure to use.
Culverson.com


LinkedIn

Blog for (mostly LabVIEW) programmers: Tips And Tricks

0 Kudos
Message 5 of 9
(5,837 Views)
Well, the docs say that the only library I need to use to access manager functions is "LabVIEW.lib". But I get link errors about not being able to find "_SetCINArraySize".

If I link with LVSB.lib as well, it links OK (finds the symbol it was looking for, but when I run it, I get "An exception occurred within the external code called by a CALL LIBRARY node..."

Even if I use a DebugBreak() first thing, it never gets there.
Steve Bird
Culverson Software - Elegant software that is a pleasure to use.
Culverson.com


LinkedIn

Blog for (mostly LabVIEW) programmers: Tips And Tricks

0 Kudos
Message 6 of 9
(5,837 Views)
It doesn't always happen, but when it does, the error is always the same: The instruction at location "0xd70a3d71" referenced memory at "0xd70a3d71". The memory could not be "read".

When it happens, it's always when I close the VI containing the CALL LIBRARY node. (Unloading the DLL ??)

If I debug from the error screen, and look at the call stack, the topmost item is "OLE32" then "LabVIEW", "LabVIEW", etc.

I am calling CoInitialize() at the beginning of my DLL and CoUnitialize() at the end, but removing those doesn't make any difference. Maybe LabVIEW already does that...

90% of the code I am using came from a C++ project I did last year, and is known to work.
Steve Bird
Culverson Software - Elegant software that is a pleasure to use.
Culverson.com


LinkedIn

Blog for (mostly LabVIEW) programmers: Tips And Tricks

0 Kudos
Message 7 of 9
(5,835 Views)
Allright, I found the problem. In doing the individual strings, I was using strcpy() to move them into the LabVIEW string handle's space. The trouble is strcpy() moves the trailing terminator, as well. So I had allocated N bytes, and used N+1. Classic bug.

Still don't know why I cannot use SetCINArraySize() though.
Steve Bird
Culverson Software - Elegant software that is a pleasure to use.
Culverson.com


LinkedIn

Blog for (mostly LabVIEW) programmers: Tips And Tricks

Message 8 of 9
(5,834 Views)

Hi, Steve

 

I have a similar issue as you had.  can you show me your solution in VI?

 

let me explain what I have:

 


I have a 3rd party library functions in dll ( from Finger Lakes Instrumentation) which I like to call in Labview. 

 

one of the functions looks like:


long FLIList (long domain, char*** names)


This function returns a pointer to a NULL terminated list of available device names. The pointer should be freed later with FLIFreeList(). Each device name

in the returned list includes the filename needed by FLIOpen()

 

Return Value: Zero on success.
Non-zero on failure.

 

Parameters:
 domain: a bitwiseORed combination of interface method and device type.
  names: Pointer to where the device name list will be placed.

 

 

I  read the article on  http://decibel.ni.com/content/docs/DOC-9079

"Calling C/C++ DLLs Containing Simple and Complex Datatypes from LabVIEW"

 

and added a function in  PassingDataSampleDLL.c as

 

void ReturningValuesByReference_2DArrayOfStrings (int rows, int strLength, char ***newArray)
{
    int  i, j;
    char  *array;
    char strArray[10][10]={"this","is","my","test", "OK",
                            "lets","see","if","it", "works" };

    array =malloc (sizeof( char) * rows* strLength);
    for (i=0; i<rows; i++)
   {
         strcpy((array + i*cols), strArray[i]);
 } 
      *newArray=array;
}

 

I am then able to read back the array of strings, see attached VIs.  But I have to give strLength and rows in order to do that correctly. 

 

The 3rd party function FLIList() has no dimension information about  the  device name list. How can I read back?  any suggestion?

 

 

 

Cheers

 

 

Xiaofeng

0 Kudos
Message 9 of 9
(5,050 Views)