11-05-2007 11:27 AM
11-07-2007
10:04 AM
- last edited on
05-28-2025
01:27 PM
by
Content Cleaner
Hi WildEnte,
Thank you for contacting National Instruments. From the information you have provided here it looks like you are on the right track with getting this application working. To go about answering some of your questions, I would like to point you to a few references that should be helpful.
You are correct in using a LabVIEW cluster to emulate a struct, but as far as passing a pointer to the struct, this is done a little differently in LabVIEW. To do this we basically have to create a reference to the cluster, and index out the elements of the array in a known order. This is shown in this discussion forum.
Some other information that might be useful to you in this process can be found in some of the resources that are searchable from ni.com/support.
It seems like you have a lot going on here, but if you break it up into individual questions it will be easier for all of us to answer for you. I hope this helps! Let me know if there is anything else I can clarify for you in LabVIEW. Have a great day!
Jason W.
11-15-2007
11:03 AM
- last edited on
05-28-2025
01:27 PM
by
Content Cleaner
First off, this problem doesn't have anything to do with control references; those have meaning only inside of the LabView environment.
My first suggestion is to look for a higher-level library that will do whatever MIDI function you are trying to call. If you can find an ActiveX or .NET object that will control the MIDI subsystem, I recommend that you use it instead since it will be a lot easier.
If calling the DLL directly is the best option (and calling DLLs is absolutely the way to go when you need high performance) then you need to understand how LabView passes data to library functions. That is described here:
https://www.ni.com/docs/en-US/bundle/labview/page/configuring-the-call-library-function-node.html
As you can see there are a lot of ways to pass data, but they boil down to this: LabView can pass, via various levels of dereferencing, a pointer to some [mostly] flat data. Things are easiest when your DLL mimics LabView's own data storage methods. For example, wiring a cluster into a node set with Adapt to Type passes a handle (struct**, pointer to pointer to [mostly] flat data). So if you are writing your own DLL, it's pretty easy to write something that works nicely with LabView data (e.g., strings with 4-byte length headers, self-describing arrays) and in fact the result is cleaner looking than its "pure C" variant because of the self-describing arrays and string length prefixes. (I say "mostly" flat because LabView clusters with variable-length elements aren't stored in a flat format.)
When it comes to matching someone else's API things get harder, and in this case you are out of luck; you are going to have to write a LabView-callable wrapper function in C because LabView can't mimic the exact data structure needed. The problem is not the struct; there is a sneaky way to pass a pointer (vs a handle) to a struct, which is to do this:
The reason this works is that a pointer is just an address; as long as all the right bytes are in memory in the right order starting at that address, all is well. The DLL doesn't know that LabView thinks it is passing a pointer to an array of bytes, it just gets the pointer to the first byte and it reads and interprets those bytes according to its own struct def. Reverse the process cooming back from the DLL (convert byte array to string, unflatten from string against the LV cluster.) This method will work for any flat struct.
In LabView 8.5 there may be a less bogus way of doing this, which is to pass the cluster in with Adapt to Type and select "Array Data Pointer" as the format. This is new and I haven't tried it.
But in your case, you want to point to a struct that contains a pointer to a string plus some numeric data and another pointer, and this you can't do directly. LabView does not expose the location of a string to you anywhere outside the Call Library Function node, so there's no way to "find the pointer" to a LabView string from outside the DLL. What you need to do in this case is write a wrapper function in C that takes your LabView data. Only then will LabView "tell you where the bytes are" and promise not to fiddle with them until the library call is complete. Once you are in C-land, you can throw pointers around to your heart's content. As long as you do it all perfectly, all will be well. (If you write one byte too many on output, boom! 🙂
There are a few ways to do this. You could make a cluster that has meaning in LabView, e.g. replace lpData with a LabView string, then pass the cluster in to your wrapper function set to "Adapt to Type"; that will pass a handle to the cluster. You can generate the .c file from the Call Lib Fuction, and it will outline where the data is.
Complications: In this case it's your job to convert the handle to the labview string (which has 4 bytes of length as a prefix and no trailing 0x00) to a C-string by malloc()'ing a new buffer and memcopying the string out. There might be something in the LV CIN tools that does this, I'm not sure. Make sure you release the new string after the MIDI call.
The lazy route (which is what I would do) is pass the string as the first arg and all of the numeric stuff as the second arg, leaving the space for lpData as you have now. (I don't know what struct "midihdr_tag" is, but if that's not something you were passed in a previous call, you'll need to add a third argument for that cluster and treat it similarly.) Then you can tell LabView that it should pass the string arg as a C-string pointer. Inside your wrapper, extract the pointers and stuff them into the data structure as needed. Dereference once (to go from handle to pointer) and make the MIDI call. To be clean, put everything back where it was before in the cluster; in particular don't believe you can pass out lpData* and do whatever you want with it in LabView; LabView will release/change that address as soon as you leave the output string unwired or do anything to it (e.g. concatenate.) You'll be creating a new string buffer and a new pointer on your next MIDI call.
All of this complication is a result of a) LabView's hiding the details of memory allocation/deallocation from you and b) LabView's internal data structures being a bit different from C conventions (e.g. LabView strings vs. C strings). The second means that even when LabView is using non-flat data structures (e.g. cluster containing an array), you can't just blindly pass them along to a C function and expect it to work right. It would be nice if NI would write a little mini-compiler into the Call Library Function that would do what our wrapper function is going to do, but that's probably a fairly significant project.
Still, each function wrapper is only going to have about 10 lines of C. You can put wrappers for all the MIDI functions you want to call into a single DLL, then ship that DLL with your app.
(Now you see why I suggested you look for an ActiveX MIDI control!)
-Rob Calhoun
11-15-2007 04:25 PM
04-24-2008 04:56 AM
Hello,
I am a graduate mechanical engineering student at CSUN, working on 3D data acquisition in mobile robotics. We have just purchased the 3D Time-Of-Flight camera SR3000, and currently I am trying to make it talk to LabVIEW 8.2.1. I have been working on modifying the example "MathScript - Using shared libraries.vi" to call the DLL functions and executing them in mathscript.
So far I was only able to get error messages inditating that it didn't find the device (err -6) or wrong device is being called (err -1). When I look at the called functions, they all require the 'struct' handle function for defining/registering the camera.
struct _CMesaDevice; //forward declaration
typedef struct _CMesaDevice SRCAM;
Overall with my limited programming knowledge I could see I wasn't properly executing the basic DLL functions such as,
int SR_OpenUSB ( SRCAM * srCam, unsigned int serialNumber )
int SR_Close ( SRCAM srCam )
The mathscript so far I could developed looks like below,
if( ~libisloaded(n) )
loadlibrary(libpath, header, 'alias', n);
end
functs = libfunctionsview(n);
%Code only gives an error msg with a numeric value such as 1
% 0 value is to return first found device at USB
Open = calllib(n, 'SR_OpenUSB', 1, 0);
Close = calllib(n, 'SR_Close', 1);
unloadlibrary(n);
I was reading similar articles but I haven't clearly understood how I could relate the information given there in to this particular issue. I have attached the header file I was using for calling the functions as well as the DLL file of the camera. Could someone please help me solve this problem? (The whole API package could be found at http://www.mesa-imaging.ch/drivers.php)
Thank you very much for your time,
Omer Ecevitoglu
04-24-2008 03:14 PM
04-30-2008 09:33 AM