LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Passing DLL a pointer to a struct and retrieving data

Hi everyone,

 

Dumb question from someone that doesn't understand structs or pointers very well...

 

I have a DLL that I need to pass a pointer to a struct. When the function runs it will continuously write data to the struct and I need to read this data. I am trying to write a wrapper VI that contains a CLFN that I can reuse. The function prototype looks like this:

 

S16BIT DECL aceMTIGetCh10DataPkt(S16BIT DevNum, PMTI_CH10_DATA_PKT *ppCh10Pkt, S16BIT Timeout)

 

For the input ppCh10Pkt I set type to 'Adapt to Type' and format to 'Pointers to Handles' in my CLFN which is how I've seen it done in other examples. PMTI_CH10_DATA_PKT is defined in the header file as

 

typedef struct _MTI_CH10_DATA_PKT
{   /* Header, 24 bytes - MTI_CH10_PKT_HEADER_SIZE */
    U16BIT    u16PktSyncPattern;
    U16BIT    u16ChannelId;
    U32BIT    u32PktLength;
    U32BIT    u32DataLength;
    U16BIT    u16SeqNumHdrVer;
    U16BIT    u16DatTypePktFlags;
    U16BIT    u16RelativeTimeCntr[3];
    U16BIT    u16HeaderChksum;
    U32BIT    u32ChnlSpecificData;
    /* Packet Body - filled in by hardware */
    U16BIT    u16MsgData[1];
}
#ifdef linux
__attribute__ ((packed))
#endif /* linux */
MTI_CH10_DATA_PKT, *PMTI_CH10_DATA_PKT;

 

In my wrapper I created a cluster of constants with each of the above data types and wired it to the ppCh10Pkt input of my CLFN. I made the cluster an input to my wrapper VI.

 

However, I'm not sure what to do next. I right clicked the other side of my CLFN and created an output indicator for ppCh10Pkt and wired this as an output to the wrapper as well.

 

Is this correct so far? I tried to run a test VI that called the wrapper VI. I wired a cluster of constants as inputs for ppCh10Pkt and both times a got nothing as an output indicator I wired. In addition, the output status of my function is an undefined error code in the DDC documentation so I figure I'm doing something pretty wrong.

 

Any help will be greatly appreciated

 

Ryan

0 Kudos
Message 1 of 7
(4,169 Views)

Can you share your code? That would make it easier to verify that you've created the struct properly. If you made "u16RelativeTimeCntr[3]" an array, that's incorrect; it should be a cluster of three U16 elements.

 

Do you have any sample code, perhaps in a text-based language?

 

The function prototype indicates you should be passing a handle - a pointer to a pointer to the struct - rather than a direct pointer to the struct. I say this because the type definition defines a pointer type, and then the function expects a pointer to that pointer type. In this case, you need to do DSNewPtr to allocate enough memory for the cluster (or struct), then pass a pointer to that allocated memory. When you get the result back, use MoveBlock to copy the data from that pointer into a LabVIEW cluster (you may have to do some byte- and word-swapping to get the correct endianness, depending on how you do it). You should NOT configure the CLFN to pass by handle, because that gets you a handle to a LabVIEW-specific data type and assumes that your DLL understands it (which it probably doesn't unless it was written specifically to work with LabVIEW).

0 Kudos
Message 2 of 7
(4,118 Views)

@nathand wrote:

Can you share your code? That would make it easier to verify that you've created the struct properly. If you made "u16RelativeTimeCntr[3]" an array, that's incorrect; it should be a cluster of three U16 elements.

 

Do you have any sample code, perhaps in a text-based language?

 

The function prototype indicates you should be passing a handle - a pointer to a pointer to the struct - rather than a direct pointer to the struct. I say this because the type definition defines a pointer type, and then the function expects a pointer to that pointer type. In this case, you need to do DSNewPtr to allocate enough memory for the cluster (or struct), then pass a pointer to that allocated memory. When you get the result back, use MoveBlock to copy the data from that pointer into a LabVIEW cluster (you may have to do some byte- and word-swapping to get the correct endianness, depending on how you do it). You should NOT configure the CLFN to pass by handle, because that gets you a handle to a LabVIEW-specific data type and assumes that your DLL understands it (which it probably doesn't unless it was written specifically to work with LabVIEW).


nathan is right about the u16RelativeTimeCntr having to be a cluster element. But your cluster is made even more nasty by having the 1 word element u16MsgData at the end which from the above remark is actually a variable sized data element at the end of the packet struct. There is no way to generate that directly in LabVIEW. You'll have to deal the data as a byte buffer and then parse it manually into its elements after you received it from the function.

 

He is also right about the function expecting a pointer to a pointer to that structure, although calling it handle in this respect is certainly not correct. It's simply a pointer to a message packet returned  by reference. Now MOST likely this function allocates this packet somewhere and then returns it to the caller (you) but it is not fully clear if it really does that or if it expects the caller to allocate that packet in the first place.  Changes are that it does since the function does not take any parameter that would tell it how big the buffer is that was allocated by the caller.

Then the next question is where would this function allocate that memory? It can allocate a memory buffere whose lifetime it manages autoamatically such as a single buffer in a global variable that gets reused for every call of this function but more common would be to allocate a new buffer for every successful call of this function. In this second case you as caller need to deallocate this buffer after you have interpreted the data in it by the means as indicated in the documentation to this function. And your DLL better provides this deallocation function. If not the programmer has fluked in a great way since heap memory management is C runtime specific and the DLL may and quite often does use a different C runtime version than what LabVIEW is using (depending on the C compiler version used to create both.

Rolf Kalbermatter
My Blog
0 Kudos
Message 3 of 7
(4,108 Views)

Thank you for the response!

 

Unfortunately my company will not let me upload code, but I can show screenshots of what I am doing. I had originally defined the struct element u16RelativeTimeCntr[3] as an array so that was incorrect. I have attached my new version which has a cluster of three u16 elements contained within the larger cluster. Should u16MsgData[1] be a cluster as well or can it just be a single element?

 

It appears I was not even close with my previous approach passing the cluster directly to the CLFN. I have attached my new attempt which calls DSNewPtr. I wasn't sure how big to make it. I added up all the bits in my cluster and divided by 8 to get a number in bytes. I haven't really found anything that goes over DSNewPtr for a beginner like myself, but I've copied the setup I've seen in other threads.

 

You said that I need a pointer to a pointer to the struct. What should I put for my getCh10DataPkt CLFN as type and data format for the pointer I pass to it?

 

I also setup a MoveBlock CLFN. I was able to find a little bit more information on this than the DSNewPtr but I'm still not sure I did it correctly. Do I pass it a cluster in the format I want my data in like I did in the screenshot or do I have to call DSNewPtr again and pass it that?

 

I apologize for such basic questions. I'm trying to learn from other forum posts but still a little out of my element here.

 

As for sample code, I don't have much, but here is an example of calling aceMTIGetCh10DataPkt in C:

 

S16BIT nResult;
MTI_CH10_DATA_PKT* pPkr;


/* Get a 1553 Data Packet */
pPkt = (MTI_CH10_DATA_PKT*) malloc(0x1000);
nResult = aceMTIGetCh10DataPkt(
     0, /* LDN */
     &pPkt, /* MT-I Packet Storage */
     -1); /* Wait forever (block) */


if(nResult)
     printf(“aceMTIGetCh10DataPkt Error: Code %d\n”, nResult);

 

 

Download All
0 Kudos
Message 4 of 7
(4,075 Views)

Thanks for the response!

 

What is not shown here is that there are several other function calls to the dll to set up our hardware. These have already been tested and seem to be working so I did not show them. I'm not sure if this is what you are talking about in your response, but when you configure the device you have to choose a buffer size that will be allocated on the device for these messages, the number of buffers it will allocate to store the data, and the size of each buffer. The function allows the use of zero copy but we are not using that and per the documentation:

 

If zero-copy buffers are disabled, then the user must allocate a local packet buffer for aceMTIGetCh10DataPkt() to copy the MT-I data packet into. The usage is as follows:

#define PKT_BUF_SIZE 0x8000 /* limits [0x1000,0x80000] */

char buf[PKT_BUF_SIZE]; /* or use malloc() */

MTI_CH10_DATA_PKT *pCh10Pkt = (MTI_CH10_DATA_PKT *)buf;

 

wResult = aceMTIGetCh10DataPkt(DevNum, &pCh10Pkt, Timeout);

 

 

 

I have not seen anything in the documentation about deallocating memory, the few examples they have seem to just repeatedly call aceMTIGetCh10DataPkt. Can you expand on what you meant when you said I would have to read u16MsgData as a byte buffer and parse it manually? How will I handle that different than the other elements in the cluster?

 

Thank you for the help

 

0 Kudos
Message 5 of 7
(4,070 Views)

@rjh123 wrote:

Thank you for the response!

 

Unfortunately my company will not let me upload code, but I can show screenshots of what I am doing. I had originally defined the struct element u16RelativeTimeCntr[3] as an array so that was incorrect. I have attached my new version which has a cluster of three u16 elements contained within the larger cluster. Should u16MsgData[1] be a cluster as well or can it just be a single element?


 

Well if your company won't allow you to upload any code, they should maybe think again. I can understand that you can't upload your whole application but I have very little interest in recreating the VIs you did. You want help here so you need to put up some effort. I'm doing this in my own free time, nobody pays me to do this, so I choose under what conditions I spend my time here!

 


 

It appears I was not even close with my previous approach passing the cluster directly to the CLFN. I have attached my new attempt which calls DSNewPtr. I wasn't sure how big to make it. I added up all the bits in my cluster and divided by 8 to get a number in bytes. I haven't really found anything that goes over DSNewPtr for a beginner like myself, but I've copied the setup I've seen in other threads.

 

You said that I need a pointer to a pointer to the struct. What should I put for my getCh10DataPkt CLFN as type and data format for the pointer I pass to it?

 

I also setup a MoveBlock CLFN. I was able to find a little bit more information on this than the DSNewPtr but I'm still not sure I did it correctly. Do I pass it a cluster in the format I want my data in like I did in the screenshot or do I have to call DSNewPtr again and pass it that?

 

 

As for sample code, I don't have much, but here is an example of calling aceMTIGetCh10DataPkt in C:

 

S16BIT nResult;
MTI_CH10_DATA_PKT* pPkr;


/* Get a 1553 Data Packet */
pPkt = (MTI_CH10_DATA_PKT*) malloc(0x1000);
nResult = aceMTIGetCh10DataPkt(
     0, /* LDN */
     &pPkt, /* MT-I Packet Storage */
     -1); /* Wait forever (block) */


if(nResult)
     printf(“aceMTIGetCh10DataPkt Error: Code %d\n”, nResult);

 

 


This API looks broken in several ways. The data buffer that gets allocated is according to your second post either 0x1000 or 0x80000 (typo?) bytes large and gets passed to the function without telling the function how big that buffer is. Such an API is bound to cause some memory buffer overflow at some point since it does not know how big the passed in buffer is.

 

In the cluster you can actually leave the last element away. You don't really are interested in that as you will have to treat the data in two junks, one the header with the first 28 bytes being the header (without that last message element) and the rest is the actuual data message.

 

Then in your other pic you should have configured the second parameter as pointer sized integer passed by pointer (you have an uInt64 there as pointer or maybe not). The MoveBlock() part looks ok although I really can't verify it as a pic hides most of the actual configuration completely. And please only copy 28 bytes not the entire buffer, otherwise MoveBlock() will overflow the data memory buffer that your cluster contains Also why would you allocate only 34 bytes when the C code clearly allocates 0x1000 (or even 0x80000) bytes there?

 

Then you need to also copy the actual message buffer data from the pointer + 28 until the end of the message, which you will probably have to determine from information in the header (u32DataLength? but that is likely in u16 data elements so you need to account for that when using the second MoveBlock which uses bytes) and you need to make sure that this second MoveBlock() has finished before you call DSDisposePtr().

 

Now all this text could have been simply applied in about half the time to the VI if you had attached it to the post and I could have verified that everything in the Call Library Nodes is correctly configured. Thanks to your company policies you have to do it yourself, it took me double the time to describe what needs to be changed, and you still don't know if your Call Library Nodes are actually correctly configured.

Rolf Kalbermatter
My Blog
0 Kudos
Message 6 of 7
(4,056 Views)

@rjh123 wrote:

Thanks for the response!

 

What is not shown here is that there are several other function calls to the dll to set up our hardware. These have already been tested and seem to be working so I did not show them. I'm not sure if this is what you are talking about in your response, but when you configure the device you have to choose a buffer size that will be allocated on the device for these messages, the number of buffers it will allocate to store the data, and the size of each buffer. The function allows the use of zero copy but we are not using that and per the documentation:

 

If zero-copy buffers are disabled, then the user must allocate a local packet buffer for aceMTIGetCh10DataPkt() to copy the MT-I data packet into. The usage is as follows:

#define PKT_BUF_SIZE 0x8000 /* limits [0x1000,0x80000] */

char buf[PKT_BUF_SIZE]; /* or use malloc() */

MTI_CH10_DATA_PKT *pCh10Pkt = (MTI_CH10_DATA_PKT *)buf;

 

wResult = aceMTIGetCh10DataPkt(DevNum, &pCh10Pkt, Timeout);

 

 

 

I have not seen anything in the documentation about deallocating memory, the few examples they have seem to just repeatedly call aceMTIGetCh10DataPkt. Can you expand on what you meant when you said I would have to read u16MsgData as a byte buffer and parse it manually? How will I handle that different than the other elements in the cluster?

 

Thank you for the help

 


Ryan,

 

Thanks to your initial efforts, I have the code working, basically. Now, it takes into account data pumps originating from either RTs or the bus controller; allows for mode code transmissions; and successfully converts the messages into the essentials of the legacy 40-word raw format. I've tried various word counts and inserted some faults; promising results for all screens.

 

Problem now is "memory leakage", a term you use when your machine becomes sluggish and you don't know exactly why unless you use a utility that, of course, we're not allowed to use. I think it's related to not releasing the memory block after processing each packet.

 

Probably have to assign a handle that's related to the pointer. First attempt at disposing of the memory block today didn't fix the problem.

 

Regards; Hoki Hy; Hook 'Em Horns .........

 

P.S.

I searched the forums and found a post by someone struggling with the selfsame issue in 2008. Sent him a private message asking for his opinion but no response so far.

 

http://forums.ni.com/t5/LabVIEW/call-library-function-node-malloc-to-recieve-a-struct/m-p/727272

 

 

Jeffrey Bledsoe
Electrical Engineer
0 Kudos
Message 7 of 7
(3,680 Views)