Jump to content

Passing array of string to C dll


Recommended Posts

Hi all,

I have to call the following function that is part of a C dll:

icon_regfilt_createRegistrationFilter (const char * extractComponent, const char ** extractSubcomponent, const float * shifts, unsigned int nSubcomponents, enum regfilt_interpolation_method_t method, BOOL copySubComponents)

How can I pass the extractSubcomponent parameter, that is an array of strings (char**)?

In C it is quite simple, this is how the function is used:

char * registrationDataFormat;

char* extractionDataFormat;

const char* subComponents[3] = {"Red", "Green", "Blue"};

float shifts[3] = {0.0f, 0.0f, 0.0f};

// Create a registration filter for the component named Color 1

registrationFilter = icon_regfilt_createRegistrationFilter("Color 1", subComponents, shifts, 3, NN, FALSE);

In LabVIEW I tried a few:

- create the array of strings in LV and pass it directly to the call library node declaring it as "Adapt to type" and "Handle by value"

- create the array of strings in LV and pass it directly to the call library node declaring it as "Adapt to type" and "Pointer to handles"

- converting in LV the array of strings to a 2D array of numbers and passing this to the Call library function node (but I can't change the function in order to pass the array sizes)

but the problem is that in LV 8.5 it always crashes and in LV 2010 the function runs and returns a pointer as it is expected, without any error, but actually it doesn't create the memory structure that should create.

Have you ever passed an array of strings to a dll? How can I do this?

Thank you very much for your help!

Edited by Reikira
Link to comment

You need to write a C wrapper function for that. LabVIEW strings are not the same as a char*, so creating an array of LabVIEW strings does something quite different than char**. Besides, char ** is rather ambiguous anyways. It could be an array of string pointers as your API expects it or it could be also a reference to a string pointer.

Basically you need to write a C function that increases every LabVIEW handle in the array with one character annd fill in the terminating 0 char there, then create an array of pointers where you fill in the string pointer extracted from the LabVIEW handle.

Something like this:

typedef struct { 	int32 len; 	LStrHandle elm[];} **LStrArrHdl;yourtype LVfunctionWrapper(....., LStrArrHdl arr, ....){   int32 i ;   char **ptr = malloc((*arr)->len * sizeof(*char));   if (!ptr)    	bailout;   for (i = 0; i < (*arr)->len; i++)   {   	int32 len = LStrLen(*((*arr)->elm[i]));   	LStrHandle h = (*arr)->elm[i];   	err = DSSetHandleSize(h, len + 1);   	if (err)   		bailout;   	ptr[i] = LStrBuf(*h);   	*(ptr[i] + len) = 0;   }   retval = yourfunction(......, ptr, ......);   free(ptr);   return retval;}

  • Like 1
Link to comment

I think you can do this in LabVIEW, although I await Rolf's expert opinion for confirmation.

post-3989-0-42553100-1311959732_thumb.pn

It might work! I'm not sure about the array of pointer sized integers though. On the LabVIEW diagram they are always 64 Bits, but possiby LabVIEW does the right thing here and only converts to a 64 Bit value when passing it to an indicator. That should be indicated with a coercion dot IMO, but it isn't.

Link to comment
  • 3 years later...

It might work! I'm not sure about the array of pointer sized integers though. On the LabVIEW diagram they are always 64 Bits, but possiby LabVIEW does the right thing here and only converts to a 64 Bit value when passing it to an indicator. That should be indicated with a coercion dot IMO, but it isn't.

Hi,

I tried it also and it really works perfectly. However, I have similar issue which I am not sure how to fix it. My C dll function has an input which requires array of pointers to double array. (double** arrayptrs). Is there any option how to solve it like in the case for array of strings ? 

Link to comment

There's some ambiguity in what you're passing there. Are you passing an array of pointers to arrays of the same length, or several different lengths (as is the case for an array of strings)? Either way you should be able to do it similar to the string array case. You will need to adjust the math for the difference in size between a char (1 byte) and a double (8 bytes), and remove the extra byte allocated for the null that terminates the string because arrays aren't terminated.

Link to comment

Yes, it's possible, although a bit complicated. Use moveblock to copy the array of addresses into a LabVIEW array, then iterate through those and use moveblock to copy the actual data into an array. Or, use the technique explained here: https://decibel.ni.com/content/docs/DOC-9091. You'll need to account for your arrays having different lengths, which you could do simply by bundling each one to generate an array of clusters where each cluster contains an array. 

Link to comment

Yes, it's possible, although a bit complicated. Use moveblock to copy the array of addresses into a LabVIEW array, then iterate through those and use moveblock to copy the actual data into an array. Or, use the technique explained here: https://decibel.ni.com/content/docs/DOC-9091. You'll need to account for your arrays having different lengths, which you could do simply by bundling each one to generate an array of clusters where each cluster contains an array. 

Thank you for quick answer. It makes sense. I suppose that the same should be possible when the C function returns the array of strings.

 

I have one more question about freeing resources. As I mentioned, the C function returns an array of pointers to double arrays of different sizes. These double arrays are dynamically allocated by malloc. How to clear this memory in the end of program ? Is it possible ans safe by DisposePtr Node ? 

Link to comment

Thank you for quick answer. It makes sense. I suppose that the same should be possible when the C function returns the array of strings.

 

I have one more question about freeing resources. As I mentioned, the C function returns an array of pointers to double arrays of different sizes. These double arrays are dynamically allocated by malloc. How to clear this memory in the end of program ? Is it possible ans safe by DisposePtr Node ? 

 

No DSDisposePtr() is not safe! Your DLL should export a function that uses internally free() to dispose those resources. One of the reason for this is that the DLL will most likely link to a different C runtime library than what LabVIEW would use, so that malloc() in the DLL works on a different managed heap than free() in LabVIEW. Also DSDisposePtr() does a little more than just calling free() and that would conflict with a memory pointer only allocated with malloc() even if the free() used by LabVIEW would operate on the same heap than the malloc() used in your DLL.

Link to comment

Yes, it's possible, although a bit complicated. Use moveblock to copy the array of addresses into a LabVIEW array, then iterate through those and use moveblock to copy the actual data into an array. Or, use the technique explained here: https://decibel.ni.com/content/docs/DOC-9091. You'll need to account for your arrays having different lengths, which you could do simply by bundling each one to generate an array of clusters where each cluster contains an array. 

 

I have some problems with implementing the code based on the instruction with the MoveBlock function. My C function which returns the array of pointers to double arrays looks like: void myfunc (double ***arrayptrs, int *size). Could you provide some small example how to dereference the pointer ?

My solution does not work..

  

Link to comment

Ah! So in this case, it's the calling code (LabVIEW) that needs to do both the allocation and deallocation. Are you doing anything to allocate the memory? If you do not first allocate the memory, the code will probably crash when it calls your function. You should do something similar to my first post in this thread, but swap the order of the calls to MoveBlock and to the DLL function, so that you are copying data out of the array after calling the function rather than copying data into the array before the DLL call.

 

I notice that your function prototype has 3 *s (double ***arrayptrs) - is this correct, or an accidental typo? If that's correct, then you need yet another layer of indirection, meaning another call to DSNewPtr and another call to MoveBlock (to move the addresses returned by DSNewPtr into an array). Is the int *size parameter an input, an output, or both? Is it an array of ints of the sizes of each double array, or a pointer to a scalar size? Do you have documentation on exactly what this function expects in the arrayptrs parameter?

Link to comment

No I do not (pre)allocate any memory. I do not know how many arrays I will have and how long the arrays will be.

 

The 2nd paragraph:

In the case of the function  ( void myfunction(double ***arrayptrs, int *size) ) when I am calling the dll from top level C code, I declare the arrayptrs (double **arrayptrs ) in my main().This is to be passed to a function to have memory allocated to it ( myfunction(&arrayptrs, &size) ). Then I do some operations and in the end I free the resources in the main(). Now I would like to replace the top level C code with LabView. The size parameter is an output specifying the count of the double arrays (length of the arrayptrs array)

Edited by charlie87
Link to comment

Ah! So in this case, it's the calling code (LabVIEW) that needs to do both the allocation and deallocation. Are you doing anything to allocate the memory? If you do not first allocate the memory, the code will probably crash when it calls your function. You should do something similar to my first post in this thread, but swap the order of the calls to MoveBlock and to the DLL function, so that you are copying data out of the array after calling the function rather than copying data into the array before the DLL call.

 

I notice that your function prototype has 3 *s (double ***arrayptrs) - is this correct, or an accidental typo? If that's correct, then you need yet another layer of indirection, meaning another call to DSNewPtr and another call to MoveBlock (to move the addresses returned by DSNewPtr into an array). Is the int *size parameter an input, an output, or both? Is it an array of ints of the sizes of each double array, or a pointer to a scalar size? Do you have documentation on exactly what this function expects in the arrayptrs parameter?

 

It could be that the function allocates the array of arrays itself and returns it and for that the pointer of pointer needs to be passed by reference. But there is absolutely no way to tell from the C prototype. The C syntax alone is notoriously inadequate to describe such details. The only way to know is to read the function documentation, consult any possible C sample that comes with the library and usually do lots of trial and error anyways since even the documentation and samples are usually to poorly made than that they would explain everything.

 

 

The 2nd paragraph:

In the case of the function  ( void myfunction(double ***arrayptrs, int *size) ) when I am calling the dll from top level C code, I declare the arrayptrs (double **arrayptrs ) in my main().This is to be passed to a function to have memory allocated to it ( myfunction(&arrayptrs, &size) ). Then I do some operations and in the end I free the resources in the main(). Now I would like to replace the top level C code with LabView. The size parameter is an output specifying the count of the double arrays (length of the arrayptrs array)

Basically the solution to free() the array in the calling program is not safe because of the possible mismatch of the C runtime library between what the DLL uses and what the caller uses. The library exposing functions that return allocated memory MUST always export a function to deallocate that memory properly.

Link to comment
In the case of the function  ( void myfunction(double ***arrayptrs, int *size) ) when I am calling the dll from top level C code, I declare the arrayptrs (double **arrayptrs ) in my main().This is to be passed to a function to have memory allocated to it ( myfunction(&arrayptrs, &size) ). Then I do some operations and in the end I free the resources in the main(). Now I would like to replace the top level C code with LabView. The size parameter is an output specifying the count of the double arrays (length of the arrayptrs array)

If you can share the C code, it will be easier to provide a direct translation into LabVIEW. It would also help to share your LabVIEW code.

 

From your description, it seems you'll want to pass a pointer-sized integer by pointer as the arrayptrs parameter. You'll get back a memory address. I assume that you either know, or can determine from the size parameter, the number of arrays pointed to by that address, so you use MoveBlock to copy that block of addresses into an array of pointer-sized integers. Then you repeat the MoveBlock process again, using the addresses you just copied, to get the actual data.

Link to comment


This is just a code example. The final C function is more complex...

MY_API void myfunction(double ***arrayptrs, int *size)

{

double testdata1[5] = {1,2,3,4,5};

double testdata2[7] = {1,2,3,4,5,6,7};

*size = 2;

/*allocate the array of pointers to jagged double arrays*/

*arrayptrs_out = (double**)malloc(*size*sizeof(double*));

/*allocate 1st double array*/

*arrayptrs_out[0] = (double*)malloc(5*sizeof(double));

memcpy(*arrayptrs_out[0], testdata1, 5*sizeof(double));

/*2nd double array*/

*arrayptrs_out[1] = (double*)malloc(7*sizeof(double));

memcpy(*arrayptrs_out[1], testdata2, 7*sizeof(double));

}

MY_API void free_arrayptrs(double **arrayptrs_out, arraycnt_out)

{

for(int i = 0; i < 0; i < arraycnt_out)

{

free(arrayptrs_out);

arrayptrs_out = NULL;

}

free(arrayptrs_out) = NULL;

arrayptrs_out = NULL;

}

int main(int argc, char* argv[])

{

double **arrayptrs_out = NULL;

int arraycnt_out = 0;

myfunction(&arrayptrs_out, &arraycnt_out);

/*do something with the arrays. The size of particular element is know.*/

free_arrayptrs(arrayptrs_out, arraycnt_out);

reutrn 0;

}

Edited by charlie87
Link to comment

This is just a code example. The final C function is more complex...

MY_API int allocateArrays(double ***arrayptrs_out, int *size)
{
	double testdata1[5] = {1,2,3,4,5};
	double testdata2[7] = {1,2,3,4,5,6,7};

	*size = 2;
	/*allocate the array of pointers to jagged double arrays*/
	*arrayptrs_out = (double**)malloc(*arraycnt_out*sizeof(double*));
	
	/*allocate 1st double array*/
	*arrayptrs_out[0] = (double*)malloc(5*sizeof(double));
	memcpy(*arrayptrs_out[0], testdata1, 5*sizeof(double));
	
	/*2nd double array*/
	*arrayptrs_out[1] = (double*)malloc(7*sizeof(double));
	memcpy(*arrayptrs_out[1], testdata2, 7*sizeof(double));

}

MY_API void free_arrayptrs(double **arrayptrs_out, arraycnt_out)
{
	for(int i = 0; i < 0; i < arraycnt_out)
	{
		free(arrayptrs_out[i]);
		arrayptrs_out[i] = NULL;
	}
	arrayptrs_out = NULL;
}

int main(int argc, char* argv[])
{

	double **arrayptrs_out = NULL;
	int arraycnt_out = 0;

	allocateArrays(&arrayptrs_out, &arraycnt_out);

	/*do something with the arrays. The size of particular element is know.*/

	free_arrayptrs(arrayptrs_out, arraycnt_out);


	return 0;
}

 

And where in this is your:

 

void myfunction(double ***arrayptrs, int *size)

If this would be your allocateArrays() then what you would do is make sure to export free_arrayptrs() too and call that from LabVIEW at the end. Also

 *arrayptrs_out = (double**)malloc(*arraycnt_out*sizeof(double*)); 

should probably be instead:

 *arrayptrs_out = (double**)malloc(*size * sizeof(double*)); 

Then in LabVIEW you configure the parameter as pointer sized integer passed by reference, then in a loop do what ned has suggested before.

 

But your proposed interface is pretty weak. While you return the number of array pointers in the returned data in *size, the caller has absolutely no way to know how many elements of data each array contains. This would be just by convention (caller and callee both have to know magically) and that is always a very bad API design.

 

And you forgot to deallocate the array of array pointers in your free_arrayptrs()!!!!

Link to comment

Hi,

 

rolfk, I am sorry for the bad code example. As I posted before, the myfunction is much more complex and I just wanted to show demonstration of some functionality which I am interested in. Anyway, I tryied to wrote it quickly, therefore there were some mistakes such as not freeing the arrayptrs_out or different function and variable names. One more time, sorry for that.

Ok, I understand the reason of exporting also free_arraptrs function.

I forgot to post that the size of double arrays which are pointed by arrayptrs_out will be know. The function returns them also, that is not any problem.

 

I am the most interested how to get the data from the arraptrs_out to LabView array where I can work with them. If somebody may post some code example where the arrayptrs_out have e.g. one element i.e. points to one double array with e.g. 10 samples (know size) it would be great....  

Link to comment

Hi,

 

rolfk, I am sorry for the bad code example. As I posted before, the myfunction is much more complex and I just wanted to show demonstration of some functionality which I am interested in. Anyway, I tryied to wrote it quickly, therefore there were some mistakes such as not freeing the arrayptrs_out or different function and variable names. One more time, sorry for that.

Ok, I understand the reason of exporting also free_arraptrs function.

I forgot to post that the size of double arrays which are pointed by arrayptrs_out will be know. The function returns them also, that is not any problem.

 

I am the most interested how to get the data from the arraptrs_out to LabView array where I can work with them. If somebody may post some code example where the arrayptrs_out have e.g. one element i.e. points to one double array with e.g. 10 samples (know size) it would be great....  

 

Something like this should probably work. But beware it's not tested in any way.

 

post-349-0-57719000-1408044902_thumb.png

Test Array Pointers.vi

Link to comment

Something like this should probably work. But beware it's not tested in any way.

 

attachicon.gifTest Array Pointers.png

 

Thank you very much, it helped me to work it out.

 

Anyway, the code is not working, I tested it. Another MoveBlock operation must be placed before the for loop to get the array of pointers to double arrays. Then the array of pointers is iterated in the for loop and the data of known size can be read.

return Double Ptrs.vi

Edited by charlie87
Link to comment

Thank you very much, it helped me to work it out.

 

Anyway, the code is not working, I tested it. Another MoveBlock operation must be placed before the for loop to get the array of pointers to double arrays. Then the array of pointers is iterated in the for loop and the data of known size can be read. I would post the code here for next generations but I am not able to find out how ....  

 

I was wondering about that when I wrote the VI, but I just didn't feel like creating a real DLL and trying it out in the debugger. Figured you could solve that problem yourself if it turned out to be necessary.  :D

 

To append something to a reply post you have to click the "More Options" button to the right of the "Post" buttion. This will give you the choice to switch into the full editor which allows to attach files (and insert images that you have uploaded as attachement). Had to search myself for that. It seems these forum board softwares like to hide that option under strange buttons names.

 

Also if you chose to edit your previous post you get into an editor with a somewhat more meaningfull button besides the "Save Changes" button called "Use Full Editor" which goes into the same editor as the other option in reply mode.

Link to comment

Awesome. Now I can finally post properly.

 

One more challenge. Any idea or suggestions how to get out of DLL to LV an array of strings ? e.g. void returnStringArray(char ***strarray, *int size)

 

There is nothing new there as far as iterating the array of strings goes. Instead of a double array you basically have a string. This is a C string in the DLL and you need to MoveBlock() into a LabVIEW handle that you have allocated before with a long enough size. Using Initialize Array with a type of U8 and passing it as C array pointer into the dst parameter of MoveBlock() will do, and after the MoveBlock() call you can convert it to a string with Bytes To String.

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.