LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Create array of LV Strings in dll

Solved!
Go to solution

Hi,

 

I would like to create an array of strings in a dll and make those accessible to labview.

 

In order to test my dll I would like to create an array of LStr in C++, but I don't know how.

 

Are there some LabView manager function I can use for that?

So far I create LVStrHandles instead, of course this is wrong.

 

typedef struct {
    int32 dimSize;
    LStrHandle elt[1];
} TD1;
typedef TD1** TD1Hdl;


int main()
{
    std::array<std::string, 5> devices{ {"         ", "         ", "         ", "         ","         "}};
    std::array<LStrHandle, 5> lvstrings;

    TD1Hdl array;
    (*array)->dimSize = 5;
    for (int i = 0; i < 5; i++) {
        (*lvstrings[i])->str=devices[i].c_str();
        (*lvstrings[i])->cnt=devices[i].size();

        (*(*array)->elt[i]) = lvstrings[i];
    }
    
    getPortsArray(array);

}

 

void getPortsArray(TD1Hdl output) {
	int i;

	std::array<std::string, 5> devices{ { "USB","COM 0","COM 1","COM 3","Ethernet" } };

	int iterations = (*output)->dimSize > devices.size() ? devices.size() : (*output)->dimSize;

	for (i = 0; i < iterations; i++)
	{
		if (!DSCheckHandle(output[i])) {

			int32 len = output[i]->dimSize;
			uInt32 stringLength = len * 2;

			if (stringLength > MaxHandleStringLength) {
				stringLength = MaxHandleStringLength;
			}
			LStrHandle s = (*output)->elt[i];
			strncpy((char*)(*s)->str, devices[i].c_str(), devices[i].size());
			(*s)->cnt = devices[i].size();
		}
	}
}
0 Kudos
Message 1 of 12
(2,350 Views)
Solution
Accepted by topic author Quiztus2

Is it correct, that you have no experience with the labview memory manager functions, no experience with labview strings and no experience with more simple labview arrays?

 

If the answer is yes, it makes really no sense to try handling an labview array of strings.

 

 

Message 2 of 12
(2,319 Views)

Thanks. This was helpful.

0 Kudos
Message 3 of 12
(2,312 Views)

As an extra hint, the LabVIEW Call Library node is purely C based. No C++ ismes whatsoever. So don’t even think about using standard template datatypes. They are unusable for multiple reason, including that they did not exist when the DLL format and the LabVIEW Call Library node was invented. But more importantly even,  there is no official ABI standard for C++ objects so calling C++ objects in a library from a different compiler than what was used to create that library is asking for huge trouble!

Rolf Kalbermatter
My Blog
0 Kudos
Message 4 of 12
(2,222 Views)
Solution
Accepted by topic author Quiztus2

Rolf,

do you know how to access the underlying str of LStrHandleArray?

 

If I write my c_str to

 

 

(*arr[i])->String-4

 

 

I get the expected result.

If I try to copy to the underlying str member

 

 

(**(*arr[i])->String)->str

 

 

 I have no luck.

 

typedef struct
{
    int32 dimSize;
    LStrHandle String[1];
} LStrHandleArrayBase;
typedef LStrHandleArrayBase** LStrHandleArray;

 

 

 

int getPortsLV(LStrHandleArray* arr, int rows, int cols) {

	Controller c;
	std::vector<std::string> ports;
	int status = c.getAvailablePorts(ports);
	if (status != EXIT_SUCCESS)
		return status;

	for (int i = 0; (i < rows) && (i < ports.size()); i++) {
		if (strlen(ports[i].c_str()) > cols)
			return 2; //NOT ENOUGH COLS
		//copy dll strings to labview str array
		//missing resize of labview array
		// 
		//this works
		strcpy_s((char*)(*arr[i])->String-4, cols, ports[i].c_str());
		//this not
		//strcpy_s((char*)((**(*arr[i])->String)->str), cols, ports[i].c_str());

	}

	return EXIT_SUCCESS;
}

 

 

0 Kudos
Message 5 of 12
(2,205 Views)

Following is what you should actually do, but I did not try to compile it so beware of typos and such:

 

#include "extcode.h"

// This prolog and epilog should be used around any typedef that is passed from LabVIEW
// to a DLL for parameters configured as LabVIEW type or Adapt to Type
#include "lv_prolog.h"
typedef struct {
    int32 dimSize;
    LStrHandle elt[1];
} LStrArrayRec, *LStrArrayPtr, **LStrArrayHdl;
#include "lv_epilog.h"

// We need to have a type descriptor that is valid for a pointer element
// so we can use it for NumericArrayResize()
#if IsOpSystem64Bit
#define uPtr uQ  // unsigned Quad aka 64-bit
#else
#define uPtr uL  // unsigned Long aka 32-bit
#endif

MgErr getPortsLV(LStrArrayHdl* arr)
{
	MgErr err;
	int idx;
	Controller c;
	std::vector<std::string> ports;
	int status = c.getAvailablePorts(ports);
	if (status != EXIT_SUCCESS)
		return someConversionToMgErr(status);

	/* If the incoming array is bigger than what we need, we first need to loop through
	   the superfluous items to see if they contain valid string handles and if so we
	   need to deallocate them to avoid memory leaks */
	if (arr)
	{
		for (idx = ports.size(); idx < (**arr)->dimSize; idx++)
		{
			LStrHandle *elt = &((**arr)->elt[idx]);
			if (*elt)
			{
				err = DSDisposeHandle(*elt);
				*elt = NULL;
			}
		}
	}
	/* Now we need to make sure the StringArrayHandle is large enough to hold all ports */
	err = NumericArrayResize(uPtr, 1, (UHandle*)arr, ports.size());
	for (idx = 0; !err && (idx < ports.size()); idx++)
	{
		LStrHandle *elt = &((**arr)->elt[idx]);
		// Resize string handle to be large enough to store the string data
		err = NumericArrayResize(uB, 1, (UHandle*)elt, ports[idx].length());
		if (!err)
		{
			MoveBlock(ports[i].c_str(), LStrBuf(**elt), ports[idx].length());
			LStrLen(**elt) = ports[idx].length();
		}
	}
	if (!err)
	{
		(**arr)->dimSize = ports.size();
	}
	return err;
}

 

And yes you need to link the according object file with the "labviewv.lib" import library from the LabVIEW cintools directory as that library knows how to get at the LabVIEW memory manager functions such as DSDisposeHandle() and NumericArrayResize().

Rolf Kalbermatter
My Blog
0 Kudos
Message 6 of 12
(2,187 Views)

You copy a string without knowing where you copy it. Hmm.. I don't want to search for any reason why corrupted memeory doesn't show the expected value 🙂

 

 

You don't know much about the array at the beginning of your C function. Are there any existiung elements? take care of this.

 

I hope that I have not forgotten any of the necessary steps.

 

I assume, that you have an empty array. you have to use the labview memeory manager functions for the following steps:

- create a new labview string handle using the correct memory size.

- copy your string into to the labview string handle

- write the correct size to the labview string handle

(take care, that the standard c string function copy the terminating 0x00 byte, so either take care of this extra byte or don't use a memory copy funtion with the correct number of bytes)

Now you have a new labview string containing your string. It must be placed into the array 

- adjust the memory size of the array handle (plus one element)

(you can also allocate a larger size, but you have to take care that there is enough space for the new string handle)

- copy the labview string handle into the array

- adjust the array size element.

 

by the way: in case of this situation I personally prefer passing pointers to handles to the C function.

 

0 Kudos
Message 7 of 12
(2,179 Views)

ok rolf was faster.

 

this also looks like "NIH (Not Invented Here) syndrome" ...

 

The code example of rolf assumes, that the incoming array has more elements than needed. So you can use the existing string handles. If it has less elements than needed, you have to create new string handles with DSNewHandle().

 

0 Kudos
Message 8 of 12
(2,146 Views)

I think the word “assumes” is not correct here. It is “prepared” to deal with the case where such a bigger array is passed in to avoid the potential of a memory leak but is also very happy if you pass in an empty array, which since it is passed by reference would be actually a NULL handle. LabVIEW has a performance improvement where empty handles can be also just a NULL handle instead of having to do a memory allocation for a 4 byte buffer to store the fact that the array has 0 elements. If such a handle is passed to external code LabVIEW only allocates an empty handle if that handle is passed by value since an existing handle can be resized but a newly allocated one could not be passed back.

 

Considering that the OP in the past tried to preallocate the array and strings on the diagram, an understandable mistake when you come from C programming but a terrible idea when you try to work with LabVIEW datatypes, I felt its a good idea to be prepared for that possibility of a LabVIEW diagram preallocated array. But in general it is always a good idea to be prepared for that as you can not really control if the diagram is not going to be modified by someone who is smart enough to be dangerous but not smart enough to realize that they are dangerous.

Also if you don’t wire an empty array constant to the left terminal of the Call Library Node parameter it is possible for the LabVIEW compiler to pass in the previous returned handle from an earlier execution. So while working with LabVIEW datatypes in external code can be potentially much more performant but the entire memory management contract that you need to sign with LabVIEW is definitely not trivial! Some things are not mentioned even in the small print! 😀

Rolf Kalbermatter
My Blog
0 Kudos
Message 9 of 12
(2,140 Views)

With all due respect but your post really is not a solution to your problem but simply a follow-up question. 😁

Rolf Kalbermatter
My Blog
0 Kudos
Message 10 of 12
(2,117 Views)