Jump to content

How to get data from a C pointer in LabVIEW?


Recommended Posts

Hi everyone.

I'm using a shared libray where it creates a array and return a pointer to this array. I would like to know how, within LabVIEW, I can access the data from this pointer.... Someone can help me? Here is the code of the C shared libray:

int *pointer(void) {
	int *array;
	array = malloc(sizeof(int) * 5);
	array[0] = 0;
	array[1] = 1;
	array[2] = 2;
	array[3] = 3;
	array[4] = 4;
	return array;
}

Thanks!

Link to comment

A few questions that help us to answer your question in the best possible way. First are you supposed to release the array from LabVIEW as well? Second do you have access to the shared library source code i.e. can you modify the code if you need to or are you stick with the current implementation. LabVIEW is more flexible regarding pointers within arguments lists than return values.

Cheers,

Tomi

Link to comment

It's often difficult to get the return data from a DLL that allocates it's own memory. LabVIEW is much easier to use, as long as it can do the memory allocation.

There is a very usefull function inside Windows kernel32.dll:

void RtlMoveMemory(void *dest, int32_t Source, int32_t length);

You could use this to copy the data from the returned address into an array you allocated in your LV app.

It works fine for me with my own cluster connected to the *dest input using "Adapt to Type" and "Handles by Value".

It should do the same if you have an array instead.

If you have access to the DLL source code, I would recommend following Tomi's advice and change the way you return the data.

I find it easiest if the DLL gets a pointer to the memory that is already allocated by LV.

Link to comment

QUOTE(silmaril @ Oct 30 2007, 07:27 AM)

It's often difficult to get the return data from a DLL that allocates it's own memory. LabVIEW is much easier to use, as long as it can do the memory allocation.

There is a very usefull function inside Windows kernel32.dll:

void RtlMoveMemory(void *dest, int32_t Source, int32_t length);

You could use this to copy the data from the returned address into an array you allocated in your LV app.

It works fine for me with my own cluster connected to the *dest input using "Adapt to Type" and "Handles by Value".

It should do the same if you have an array instead.

If you have access to the DLL source code, I would recommend following Tomi's advice and change the way you return the data.

I find it easiest if the DLL gets a pointer to the memory that is already allocated by LV.

Thanks for the answer!

Tomi, yes, I release the array from LV to the shared lib. Also have the source code, so I can change it.

Well, silmaril, I'm using LabVIEW for Linux, so, there is no way to use the function inside Windows...

Okay, if is better to the shared library get a pointer to a data allocated by LabVIEW, how could I send this pointer? As you said, after the creation of the array in LV, I use a cast to a type to get the pointer for this array? I'm sorry, but I don't understood well this point.

Cheers,

Link to comment

Now that you have the source code of the share library, the simplest thing you can do is use LabVIEW memory management C functions. So pass an empty LabVIEW I32 array to your C function as an argument. Select pointers to handles as data passing method when configuring the library call from LabVIEW. This passes your LabVIEW array handle to your C code as a pointer to LabVIEW array handle. An array handle is a pointer to pointer to the actual array data. The first d elements (long) specify dimension sizes for a d dimensional array. The dimension sizes are followed by the actual array elements.

Now that you receive the array handle as an argument to your C function, you can resize the array handle to meet your needs using the following LabVIEW C memory management function. See LabVIEW help for detailed information on the function usage and arguments.

MgErr NumericArrayResize (int32 typeCode, int32 numDims, Uhandle *dataHP, int32 totalNewSize)

This allocates memory for your LabVIEW array according to what you specify and depending on the datatype you use. After you have allocated memory for you LabVIEW array, you should set the dimensions of your array. Simply write the dimensiondata to the struct I described above to the position in memory determined by dataHP returned by NumericArrayResize. Now your array is LabVIEW compatible. Then write the array content. After your C call exits, your LabVIEW code sees the new resized array and LabVIEW can even automatically dispose it.

This was a rather short introduction to quite a complicated concept. See the following to sections Fundamentals / Calling Code Written in Text-Based Programming Languages and VI and Function Reference / Connectivity VIs and Functions / Code Interface Node Functions / Memory Manager Functions from your LabVIEW help.

The bottom line is that you can allocate LabVIEW datatypes from your C code and this is the technique you should prefer. You shouldn't under normal circumstances pass C pointers to LabVIEW but to write or copy your data into LabVIEW compatible memory model and pass this back to LabVIEW. The only case where I think C pointers need to be passed to LabVIEW are when you are using C pointer to refer to some sort of C object and are using the pointer only to refer later on to the same object by passing the pointer back to your C code. In this case your pointer acts as if it's a reference and LabVIEW doesn't directly use the reference but only passes it back to C code when it needs to interact with the referenced object.

Cheers,

Tomi

Link to comment

QUOTE(Giseli Ramos @ Nov 6 2007, 07:22 PM)

It is possible to use the function MgErr NumericArrayResize in a Library Node? I don't want to use CIN...

All memory management functions are available for shared libraries. CINs are not needed and I don't recommend using CINs. You can even call memory management functions from LabVIEW directly as if they were exported functions in a DLL by using LabVIEW.exe as the library in a library node for the development environment and the corresponding runtime DLL (LVRT.dll or something) for the runtime environment.

Tomi

Link to comment

QUOTE(Tomi Maila @ Nov 6 2007, 02:52 PM)

All memory management functions are available for shared libraries. CINs are not needed and I don't recommend using CINs. You can even call memory management functions from LabVIEW directly as if they were exported functions in a DLL by using LabVIEW.exe as the library in a library node for the development environment and the corresponding runtime DLL (LVRT.dll or something) for the runtime environment.

Tomi

Better yet! Just use LabVIEW alone as library name, watch out this one is case sensitive! And then you won't have to worry about lvrt.dll or labview.exe depending on if you build an application or not.

Rolf Kalbermatter

Link to comment
  • 2 weeks later...

So, 8.5 can now handle (wrap) dll functions which

require as parameters, say, a pointer to a structure,

one of elements of which is a pointer to a variable

length data. Great! For this purpose the following

library is utilised by the DLL Import Wizard (that's

how I discovred it): \vi.lib\Utility\importsl

It has DSNewPtr.vi to create a pointer (and allocate

memory I would assume), DSDisposePtr.vi to (you

guessed!) dispose of the pointer (and deallocate

memory), InitStr.vi to write data to the memory to

which the pointer points and, the

GetValueByPointer.xnode (located in the

GetValueByPointer subfolder, can be dragged onto BD

from explorer, just like a VI, but you can't even open

its FP) to retreive the data from the memory location

the pointer points to.

No documentation though, even in the context help for

those top level VIs. So, Anybody has any idea what

PackType parameter of the GetValueByPointer.xnode is?

Well, what I said would be especially beneficial if on

its output wire GetValueByPointer.xnode really has the

reference to (address of) the data, not to a copy it

creates.

They apparently can be used to ensure that passing

of (large) data is done by reference (only the pointer

is passed around without moving/copying the data

itself) not only betweeen LabVIEW code and dll code but also

between any of your VI's as well.

Well, what I said would be especially beneficial if on

its output wire GetValueByPointer.xnode really has the

reference to (address of) the data, not to a copy it

creates.

Interesting: the default string for the InitStr.vi is

"Import Shared Library Tool in Jupiter." I wonder what

"Jupiter" is/was. My guess is it was the code name

for one of the LabVIEW versions.

Link to comment

QUOTE(hviewlabs @ Nov 20 2007, 08:14 PM)

So, 8.5 can now handle (wrap) dll functions which

require as parameters, say, a pointer to a structure,

one of elements of which is a pointer to a variable

length data. Great! For this purpose the following

library is utilised by the DLL Import Wizard (that's

how I discovred it): \vi.lib\Utility\importsl

It has DSNewPtr.vi to create a pointer (and allocate

memory I would assume), DSDisposePtr.vi to (you

guessed!) dispose of the pointer (and deallocate

memory), InitStr.vi to write data to the memory to

which the pointer points and, the

GetValueByPointer.xnode (located in the

GetValueByPointer subfolder, can be dragged onto BD

from explorer, just like a VI, but you can't even open

its FP) to retreive the data from the memory location

the pointer points to.

No documentation though, even in the context help for

those top level VIs. So, Anybody has any idea what

PackType parameter of the GetValueByPointer.xnode is?

Well, what I said would be especially beneficial if on

its output wire GetValueByPointer.xnode really has the

reference to (address of) the data, not to a copy it

creates.

They apparently can be used to ensure that passing

of (large) data is done by reference (only the pointer

is passed around without moving/copying the data

itself) not only betweeen LabVIEW code and dll code but also

between any of your VI's as well.

Well, what I said would be especially beneficial if on

its output wire GetValueByPointer.xnode really has the

reference to (address of) the data, not to a copy it

creates.

Interesting: the default string for the InitStr.vi is

"Import Shared Library Tool in Jupiter." I wonder what

"Jupiter" is/was. My guess is it was the code name

for one of the LabVIEW versions.

Those VIs were not designed (yet) for general use but are used by the Library Import Wizard

in 8.5 which should support functions parameters to structures containing variable sized data.

All the functions except the xnode are really just wrappers around LabVIEW memory manager

functions.

What is done through the library import wizard has been of course possible in LabVIEW since

about 5.x and I did use it myself a few times but I went with purpose never into the details

about how this has to be done, because of several reasons.

- It is very complicated and even when explained step by step I would not expect anyone

without a very good C knowledge to be able to understand.

- Those that can understand this can also do it themselves.

- The necessary work is tedious to do, complicated, error prone and oh well, throw in whatever

you do not like about programming ;-)

- For anything but a very simple case with one or two functions with a parameter with one or

two pointers in it at most, it is a LOT more work than writing a wrapper DLL and it is a complete

pain in the ###### to maintain.

As to your question about PackType, I could imagine it could have something to do with memory

alignement of structure elements. As such it would be a number that would be 1, 2, 4, 8, or 16.

As to passing around references inside LabVIEW: You only can do that when you represent that

reference as a pointer (i.e. int32/64) but not as a native variable sized LabVIEW datatype. Dataflow

simply mandates that memory objects that can be resized get copied at wire branches, unless the LabVIEW compiler can optimize them in such a way that branches that consume the wire only fo reading without reuseing them are executed first. Doing an advanced optimizer that could go around that across structure boundaries might be theoretical possible, although I don't think it is currently done, but a macro optimizer that could deal with this task across (possibly dynamically related) subVI boundaries would be most probably a o^n problem or worse.

Rolf Kalbermatter

Link to comment

So, can 8.5 call the functions from the library like below or not (HRESULT=long)?

typedef struct tagPoogleMsgS

{

BYTE deviceID;

BYTE priority;

BYTE protocolID;

WORD payloadLen;

BYTE *pPayload;

} PoogleMsgS;

__declspec(dllexport) HRESULT SendMessage(PoogleMsgS sMsg);

__declspec(dllexport) HRESULT OpenUsbPort(void);

__declspec(dllexport) HRESULT CheckUSB(void);

__declspec(dllexport) HRESULT RemoveConnectionlessMsg(PoogleMsgS* pMsg);

__declspec(dllexport) HRESULT RemoveMessage(PoogleMsgS* pMsg);

__declspec(dllexport) HRESULT CheckCOM(void);

__declspec(dllexport) HRESULT OpenComPort(int nCom, DWORD dwBaud);

The DLL Wizard does claim it finished successfully, but for the SendMessage it creates a node where instead of one input for the structure separate inputs are created for all the individual components of the structure plus some "dummy_controls", and pPayload is just U32 passed by value!

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.