Jump to content

Need the Opposite of MoveBlock


Recommended Posts

So I'm dealing with calling some DLLs for external communication.  When you call the DLL one function returns a pointer to an array of data.  Using the MoveBlock I can successfully get the array of data back into LabVIEW.  But one issue I'm having is that I also need to go the opposite way.  I have an array of data in LabVIEW and I need to send it to the DLL.  I realize the normal way you would do this is by calling the DLL with the Array Data Pointer on the Call Library Node.  The problem is the data I need to reference, is actually a reference in the reference.  In LabVIEW I have an array of U8 like this:

[0]=0xFF

[1]=0xAB

[2-5]=Pointer to Data

[6, 7] = 0x1234

I'm already calling the DLL passing in the array of 8 bytes using the Array Data Pointer, but within that array needs to be 4 bytes, referencing another pointer for another array of data.  Is there a way to create a pointer in LabVIEW to some array of data?  Then I can put it in my array, and send that?  Sorry my C and DLL calling experience isn't as good as my LabVIEW experience and even finding the words to search for are difficult since it returns all kinds of information about MoveBlock, and LabVIEW control references.  Thanks.

Edit: Okay looks like I might need DSNewPtr to make a pointer, that I can then pass into the DLL.  It is partially working, it just isn't the data that I put into the pointer.

  • Like 1
Link to comment
5 minutes ago, Neil Pate said:

😂 The glass is definitely half-full

Thanks, I did figure it out.  Using the linked example was very helpful.  First I think the pointer wasn't being put at the right index, so data was being read at an address and then that was being sent.  Then the example used I32 not U8 as the data of the array, and there were extra zeros between data points.  Seems all good now.

Link to comment
14 hours ago, hooovahh said:

Thanks, I did figure it out.  Using the linked example was very helpful.  First I think the pointer wasn't being put at the right index, so data was being read at an address and then that was being sent.  Then the example used I32 not U8 as the data of the array, and there were extra zeros between data points.  Seems all good now.

Glad you found the solution. It definitely is THE right approach if you want to avoid going into C code yourself. Just watch out about bitness. This does not work without conditional compiled structure if you want to make it 32/64-bit compatible.

Link to comment
13 hours ago, mcduff said:

This is way out of my league, and not sure if I understand correctly, but this link shows how to get the pointer to a LabVIEW array. Not sure if you have seen this already.

This works but is bound with troubles. A LabVIEW array is dynamic as LabVIEW is a fully managed programming environment. No it is not .Net managed, at the time the LabVIEW developers designed the basics that are valid until today, .Net was not even an idea on earth, let's not talk about a fact. But it is managed and the LabVIEW runtime handles that all behind the curtains for you.

This means that a LabVIEW variable, and especially a handle that arrays and strings are, is only guaranteed to be valid for the duration of the Call Library Node call itself. After that node returns, any passed in array, string or even scalar variable can at any point be resized, relocated or even simply get deallocated. So the pointer that you get in this way can very well get invalidated immediately after that Call Library Node returns. For performance reasons, LabVIEW tries to maintain arrays and strings for as long as possible when it can, but to decide if it can and if it propritizes this rule above other possible rules to improve performance is a tricky business and can even change between LabVIEW versions.

It is pretty safe to assume that an array or string wire that you wire through a Call Library Node, doesn't branch into other nodes and is wired to the end of the current diagram structure, is left untouched for the duration of this diagram structure. But even that is not something the LabVIEW management contract guarantees. It's just the most prudent thing to do in almost any case to not sacrifice performance. Once you have a branch in the wire before or after the Call Library Node to retrieve the internal data pointer in the handle, or you do not wire the array data to the diagram structure border, all bets are open to if and when LabVIEW may decide to modify that handle (and consequently invalidate the data pointer you just got).

Edited by Rolf Kalbermatter
  • Thanks 2
Link to comment
3 hours ago, Rolf Kalbermatter said:

This works but is bound with troubles. A LabVIEW array is dynamic as LabVIEW is a fully managed programming environment. No it is not .Net managed, at the time the LabVIEW developers designed the basics that are valid until today, .Net was not even an idea on earth, let's not talk about a fact. But it is managed and the LabVIEW runtime handles that all behind the curtains for you.

Aw man, you're telling me an undocumented feature that can make my program perform better, but also unstable, isn't the right approach.  Fine I'll stick with the CLNs.  But it is very tempting to use this node, and just lock down the VI and put up all kinds of warnings.

About the 32/64 bit through.  So on my 64 bit Windows, but 32 bit LabVIEW, the memory pointers are always 64 bit, and but it looks like it is always only using 32 bits for the address.  This is using the "Integer Sized Pointer" for the nodes.  This is apparently an I64.  Shouldn't it be an I32, and only on 64 bit LabVIEW, be an I64?  Also these should probably be unsigned right? But likely doesn't change the functionality? 

Link to comment
47 minutes ago, hooovahh said:

About the 32/64 bit through.  So on my 64 bit Windows, but 32 bit LabVIEW, the memory pointers are always 64 bit, and but it looks like it is always only using 32 bits for the address.  This is using the "Integer Sized Pointer" for the nodes.  This is apparently an I64.  Shouldn't it be an I32, and only on 64 bit LabVIEW, be an I64?  Also these should probably be unsigned right? But likely doesn't change the functionality? 

No no! The pointer size depends on the application environment, not the kernel environment. As long as you stay in 32-bit LabVIEW, pointers will be 32-bit no matter what Windows version you are in. But!!!!! Be prepared! Windows is the only LabVIEW platform that is still 32-bit too. (Well ok LabVIEW RT on ARM is also 32-bit but that is an entirely different story). All other platforms (Mac and Linux) have NO 32-bit version of LabVIEW anymore since around LabVIEW 2017. And the LabVIEW for Windows 32-bit countdown has certainly started already. Once NI has fully ported every Toolkit to 64-bit (or discontinued it) expect the LabVIEW 32-bit version to be discontinued within a year or two at most. So if they manage to get the cRIO and myRIO support finally updated to 64-bit (it's about time since at least 5 years), and the Linx Toolkit is 64-bit too, it's bye bye 32-bit LabVIEW.

The DSNewPtr() is the right approach to use. You are basically managing your own memory according to the requirements of the API that you call, since the LabVIEW management contract isn't compatible with it, and it couldn't be compatible with all the possible ways memory can be handled. .Net Interop has a whole slew of support functions to try to deal with such situations and even that isn't always sufficient to provide a solution for every possible situation without very involved and convoluted code constructs. It's the main crux of trying to marry different APIs together.

Edited by Rolf Kalbermatter
Link to comment

Generally I agree with Rolf here. That ArrayMemInfo node even though looks neat and easy-to-use could easily be removed in the future versions of LabVIEW as it's for internal use only. NI has already removed many undocumented or obsoleted stuff from the core of LV 2022, including all the NXG helper functions like that NCGGetOperateDataPointer. If it goes to production, I'd prefer conventional Memory Manager functions like DSNewPtr and friends.

Link to comment
2 hours ago, Rolf Kalbermatter said:

No no! The pointer size depends on the application environment, not the kernel environment. As long as you stay in 32-bit LabVIEW, pointers will be 32-bit no matter what Windows version you are in.

That's what I thought, which is why I was confused by the datatype that the DSNewPtr returned for an Integer Sized Pointer as a U64.

image.png.d258ddaf4856ec99091a9aea93aab20f.png 

Link to comment
16 hours ago, hooovahh said:

That's what I thought, which is why I was confused by the datatype that the DSNewPtr returned for an Integer Sized Pointer as a U64. 

That's either a very old DSNewPtr from NI (before LabVIEW 2009 which introduced support for 64-bit code and according pointer sized integers) or a non-official user created CLN. This should be a pointer sized integer since the definition for the size parameter changed with LabVIEW 2009 from an int32 to a size_t just as with the MoveBlock. It shouldn't really cause trouble though since that int32 is anyhow sign extended to a 64-bit stack parameter on 64-bit LabVIEW, and technically a LabVIEW pointer can't really span more than 2^32 bytes without causing other related problems deep down in the memory manager.

The return value of the DSNewPtr() seems to be correctly configured as pointer sized integer and definitely needs to remain that way. It is not where you potentially have to use the conditional code structure. The value for the pointer size to allocate might however have to be adjusted. As long as the structure only contains the pointer you simply can allocate 8 bytes and treat it that way, effectively having the upper 4 bytes be unused in the 32-bit case. Once that structure gets more complex and/or the pointer offset is not at 0 however or there follows data in the structure beyond the pointer, you have to adjust the whole offset and size values according to the bitness, to not cause it to overwrite the wrong location.

Basically when you configure a Call Library Node parameter to be a pointer sized variable, LabVIEW will treat it as 64-bit integer on the diagram but do the RIGHT thing depending on the bitness it is running on. It will use the entire 64-bit when it is running as 64-bit process, and the lower 32-bit when it is running as 32-bit process. And a returned value will be sign extended (if you use a signed pointer sized integer) when running on 32-bit and zero padded (if you use an unsigned pointer sized integer) on 32-bit. Nothing special needs to be done when running on 64-bit.

This is because LabVIEW is a strictly typed compile time environment and the developers wanted to keep the flattened format consistent across platforms. If they would have chosen for a special pointer datatype that is to be sized according to the current environment, flattening such structures and variables would cause significant problems and the flattening of data is not only used when you explicitly add a Flatten or Unflatten node in your diagram but a very fundamental part in many locations in LabVIEW including the entire VI Server interface but also in some areas of handling the connector pane of VIs.

Edited by Rolf Kalbermatter
Link to comment
1 hour ago, Rolf Kalbermatter said:

And really a nitpick but your title is rather inaccurate :-).

There is no opposite of MoveBlock(). The only opposite you can have is by swapping the source and target pointer here. Other than that I'm not sure what other opposite you could think off here. 

Well I was having a hard time coming up with the words of what I actually needed.  In my mind MoveBlock took a pointer to an array returned from a DLL and gets the actual array of data in LabVIEW.  I wanted the opposite, which was to take an array of data from LabVIEW, and get a pointer for it.  I agree the title is terrible, and MoveBlock can do both operations, with the only extra needed is allocating the memory with DSNewPtr, and releasing the memory with DSDisposePtr.

4 hours ago, Rolf Kalbermatter said:

Basically when you configure a Call Library Node parameter to be a pointer sized variable, LabVIEW will treat it as 64-bit integer on the diagram but do the RIGHT thing depending on the bitness it is running on. It will use the entire 64-bit when it is running as 64-bit process, and the lower 32-bit when it is running as 32-bit process. And a returned value will be sign extended (if you use a signed pointer sized integer) when running on 32-bit and zero padded (if you use an unsigned pointer sized integer) on 32-bit. Nothing special needs to be done when running on 64-bit.

That totally makes sense now.  Thanks.

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.