Giseli Ramos Posted October 30, 2007 Report Share Posted October 30, 2007 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! Quote Link to comment
Tomi Maila Posted October 30, 2007 Report Share Posted October 30, 2007 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 Quote Link to comment
silmaril Posted October 31, 2007 Report Share Posted October 31, 2007 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. Quote Link to comment
Giseli Ramos Posted October 31, 2007 Author Report Share Posted October 31, 2007 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, Quote Link to comment
Tomi Maila Posted October 31, 2007 Report Share Posted October 31, 2007 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 Quote Link to comment
Rolf Kalbermatter Posted November 2, 2007 Report Share Posted November 2, 2007 QUOTE(Giseli Ramos @ Oct 30 2007, 08:51 AM) 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. The RtlMoveMemory function does not have to be a problem for you. LabVIEW also exports a MoveBlock() function that you can access with the Call Library Node. There is an example at http://zone.ni.com/devzone/cda/epd/p/id/3672 that shows exactly this and it works on every LabVIEW platform. But since you have the source of your shared lib, go for the changed API. It's clearer! Rolf Kalbermatter Quote Link to comment
Giseli Ramos Posted November 7, 2007 Author Report Share Posted November 7, 2007 It is possible to use the function MgErr NumericArrayResize in a Library Node? I don't want to use CIN... Rolf, thanks for the hint! I will study it. Quote Link to comment
Tomi Maila Posted November 7, 2007 Report Share Posted November 7, 2007 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 Quote Link to comment
Rolf Kalbermatter Posted November 7, 2007 Report Share Posted November 7, 2007 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 Quote Link to comment
Giseli Ramos Posted November 8, 2007 Author Report Share Posted November 8, 2007 Many thanks! Okay, I understand the example for one dimension, now, I'm trying for an array of two dimensions. Let's see if it will go... Cheers, Quote Link to comment
hviewlabs Posted November 21, 2007 Report Share Posted November 21, 2007 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. Quote Link to comment
Rolf Kalbermatter Posted November 23, 2007 Report Share Posted November 23, 2007 QUOTE(hviewlabs @ Nov 20 2007, 08:14 PM) So, 8.5 can now handle (wrap) dll functions whichrequire 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 Quote Link to comment
hviewlabs Posted November 29, 2007 Report Share Posted November 29, 2007 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! Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.