Jump to content

Referencing an array through a .NET struct


Recommended Posts

Hi,

I'm using a third-party USB driver in the form of a .NET assembly DLL to talk to an instrument we are building. The driver's documentation provides a C# example for implementing data transfers that make the best use of available bandwidth on the USB. I am attempting to duplicate the example's functionality in LabVIEW, but this involves an oddball pointer type conversion that does not seem to translate very well into the LabVIEW paradigm (i.e. I've tried everything I can think of, but I can't figure it out).

The specifics are as follows:

The driver provides Begin/WaitFor/Finish-transfer methods that all require an array of event-references(?) to be passed between them. This array is a 1D array of 20 bytes. In the C# example I'm working from, the event-reference is written-to/read-from the array through a struct object provided by the driver. The struct serves as a "map" to expose the byte array as a set of U32 values that I can get/set through a .NET Property Node.

1) How do I reference the array through the struct object in LabVIEW? See the contents of the ZIP file.

Alternatively, I could avoid using the struct object altogether in LabVIEW by simply using the Type Cast function for the bytes[]/U32/reference conversion. The problem with this, however, is that I'm not entirely sure which 4 bytes represents which U32 value or what order the bytes are in. In order to find out for sure on my own, I need question #1 answered, so I can set a value and see how the array changes. Otherwise, if I can't use the struct object, can someone tell me...

2) Given the info provided, where in the array can I assume the "hEvent" struct member to be (and its byte order)?

If anyone could point me in the right direction, it would be much appreciated.

Thanks.

Link to comment

QUOTE (andymatter @ Sep 19 2008, 06:09 PM)

Hi,

I'm using a third-party USB driver in the form of a .NET assembly DLL to talk to an instrument we are building. The driver's documentation provides a C# example for implementing data transfers that make the best use of available bandwidth on the USB. I am attempting to duplicate the example's functionality in LabVIEW, but this involves an oddball pointer type conversion that does not seem to translate very well into the LabVIEW paradigm (i.e. I've tried everything I can think of, but I can't figure it out).

The specifics are as follows:

The driver provides Begin/WaitFor/Finish-transfer methods that all require an array of event-references(?) to be passed between them. This array is a 1D array of 20 bytes. In the C# example I'm working from, the event-reference is written-to/read-from the array through a struct object provided by the driver. The struct serves as a "map" to expose the byte array as a set of U32 values that I can get/set through a .NET Property Node.

1) How do I reference the array through the struct object in LabVIEW? See the contents of the ZIP file.

Alternatively, I could avoid using the struct object altogether in LabVIEW by simply using the Type Cast function for the bytes[]/U32/reference conversion. The problem with this, however, is that I'm not entirely sure which 4 bytes represents which U32 value or what order the bytes are in. In order to find out for sure on my own, I need question #1 answered, so I can set a value and see how the array changes. Otherwise, if I can't use the struct object, can someone tell me...

2) Given the info provided, where in the array can I assume the "hEvent" struct member to be (and its byte order)?

If anyone could point me in the right direction, it would be much appreciated.

Thanks.

OK, a couple of things I think I can answer - in your VI example, you show an attempt to cast a .NET object created from an array to the struct - that won't work because the target struct is not an array - LV only knows how to create objects of the specified types listed in the help.

Second, the actual layout of the data in the OVERLAPPED struct will always be exactly in the order described and will be packed as efficiently as possible (on byte boundaries) - that's what the

[structLayout(LayoutKind.Sequential,Pack=1)]

attribute means in the struct definition (http://support.microsoft.com/kb/922785). So you can always be sure the same data will be in the same place in the array. So in your alternative 3 in your LabVIEW code this will populate the correct location, although it is easier to create the buffer as U32's and then just address the array position. As far as byte order, it will be little-endian on little-endian machine (intel). But I'm not sure that really helps very much.

Once you get past that, this starts to get tricky because this is "unsafe" C# code in the example - once you create an array of structs that contain hEvent handles as the last data element, now you have to use that in the code and make sure they stay in place. In the C# example, the array of OVERLAPPED structures uses the "fixed" keyword to make sure that the data array doesn't get arbitrarily garbage collected by .NET. You'll have to do the same thing with the data array you create in LabVIEW. So this starts to look like calling into a regular (unmanaged) DLL. This thread (http://forums.lavag.org/DLLs-that-expect-LabVIEW-data-to-stay-put-t8608.html) has some info about how to fix a block of memory in labview.

So, what may work is to

1) Create an array of uints - the array should be mutilples of five

2) populate every fifth element with an event handle using the Pinvoke.CreateEvent (or you could bypass the double indirection of LV->.NET->WinAPI by calling CreateEvent from the kernel32.dll)

3) Use the LV memory manager (from the LAVA thread above) to create a block of fixed memory and copy the array into it

4) cast the pointer to this memory block into the .NET ref for the OVERLAPPED struct array

5) Watch for memory leaks - you now have lots of potential to create them

All this is pure speculation so take it FWIW ;)

However, I read this in the CyUSB help

"Again, the use of BeginDataXfer, WaitForXfer, and FinishDataXfer is the difficult way to transfer data to and from a USB device. This approach should only be used if it is imperative that you squeeze every last bit of throughput from the USB."

Sounds to me like there's an easier to use synchronous method available. You can probably build your LabVIEW code on the synchronous calls and then add asynchronous capabilities easier using reentrant VIs and the VI server to launch multiple communication threads and don't fool with the internal asynchronous calls unless you absolutely have to.

Mark

Link to comment

:worship:

Wow. Thanks for all the great ideas!

I haven't used external code before now, so this facet of LabVIEW is new to me, along with all the memory-management considerations. I have already tried using the easy synchronous XferData() method, but the resulting data throughput wasn't satisfactory, so that's why I'm heading in the more complicated route. I really like your idea of calling this method in multiple reentrant instances (and I'll try it), but there is talk of changing our data (USB) endpoint from a bulk type to an isochronous one, meaning that I may be stuck with the asynchronous Begin/WaitFor/Finish methods. Oh well, I'll get there one way or another. :D

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
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
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.