Calling C/C++ from LabVIEW

My team has been struggling with getting some higher-order functions working in LabVIEW, and we wanted to have the ability to link C/C++ code into our LabVIEW diagrams. Although others have posted with a little information, I’m hoping to provide a step-by-step process for getting this working on your own.

This is all from the tutorial posted at http://zone.ni.com/devzone/cda/tut/p/id/5694

  1. Download the vxworks63gccdist.zip file.

  2. Inside the zip file, there will be a gccdist folder. Place this folder in your C:\ drive.

  3. Open up your Control Panel and go to the System Control Panel.

  4. Go to the Advanced tab and edit Environment Variables.

  5. Under System Variables, edit the PATH variable.

  6. Append the following to the PATH variable:

;C:\gccdist\WindRiver\gnu\3.4.4-vxworks-6.3\x86-win32\bin;C:\gccdist\WindRiver\workbench-2.5\x86-win32\bin;C:\gccdist\WindRiver\setup\x86-win32\bin

  1. Make a new system variable WIND_HOME with the following value:

C:\gccdist\WindRiver

  1. Make another new system variable WIND_BASE with the following value:

C:\gccdist\WindRiver\vxworks-6.3

  1. Create a folder C:\cpp

  2. Download the attached file, named Makefile, and save it in that new C:\cpp folder. Because of the way Chief Delphi handles posted files, you have to rename the file from Makefile.txt to just plain Makefile. Beware about hidden extensions on Windows!

  3. Edit the Makefile to list the C and C++ files you wish to compile: On line 21, change your_file_here and your_other_file_here to be the base names of your .c and .cpp files. If you have more than two files, you can just list them in a similar manner, all separated only by spaces. (Note: Your file names must not have spaces.) Or, you can choose just to have one file by deleting the second entry. Note that each name on this line should end in .o, not .c or .cpp.

  4. Edit the Makefile to give it the desired name of your compiled file. This is done on line 24, and the file name should end in .out.

  5. Write your C and C++ code. Store all the .c and .cpp files in your C:\cpp folder.

  6. After writing your code, you need to decide what functions will be accessible from LabVIEW. There are a few rules for these functions:

  • They must not be overloaded. (An overloaded function is a function that has the same name as another but with different parameter types.)
  • They must not be member functions of a class. You can, however, call member functions from the function that LabVIEW sees.
  • All parameters and the return value must be primitive types (such as int or double or long) or pointers to void (void*). You cannot pass structures (clusters in LabVIEW) directly – you must pass pointers to void and then cast in your C/C++ code.
  1. If your functions are in C++ code, you must insert the following before your function:
    extern “C”
    For example, here is a C++ function:
    extern “C” int compute(unsigned int a, void* b)

If your code is C code (in a .c file, not a .cpp file), you do not need the extern “C”

  1. From your Start menu, choose Run… and enter
    cmd
    and press enter. The command window appears.

  2. Navigate to your C:\cpp folder by typing this:
    cd \cpp

  3. Compile your files by typing this:
    make
    You may get warnings about WIND_LIC_PROXY or WIND_HOME. That’s fine.

  4. Now, you need to manually upload your compiled .out file using FTP. Go to your Start menu, choose Run… and type the following and press enter:
    ftp://10.xx.yy.2/ni-rt/system
    where xx and yy are your team number, as per usual.

  5. Copy the compiled .out file (in C:\cpp\PPC603gnu) into the window that appears (which is a view of the files in the ni-rt/system folder on the cRIO). The cRIO now has your compiled code.

  6. Now, you need to connect your LabVIEW program with your newly-minted C/C++ code. Choose the spot in your code you wish to call your C/C++ code. Right-click, go to the Connectivity sub-menu, then to the Libraries & Executables menu, and then choose a Call Library Function Node. Place the node.

  7. Double-click the Call Library Function node to configure it.

  8. For Library name or path:, insert the name of the compiled code, but end with ., not .out. According to the default in the attached Makefile, this would be
    compiled_program.

    Do NOT browse for the file. That will NOT work.

  9. Enter the function name you want this node to call.

  10. Click on the Parameters tab.

  11. Here, you specify all the types of parameters and the return value. The pull-down menus are fairly straightforward, but the following tips may be helpful:

  • char is 8 bits.
  • short is 16 bits.
  • int is 32 bits.
  • long long is 64 bits.
  • float is “4-byte single”
  • double is “8-byte double”
  • If you’re not passing a numeric, use “Adapt to Type”. “Handles by value” is good for Data format.
  1. When you’re done specifying types, click OK to close the box.

  2. Wire the inputs as you would expect. The Call Library Function Node forwards all inputs to outputs, but the only generally useful output is the one labeled “return type”.

  3. Save and deploy. It should work as desired.

  4. As you open and close VIs with Call Library Function nodes, LabVIEW may start searching for DLL files for your functions. These DLLs do not exist, so you have to manually tell LabVIEW to ignore the files. This isn’t an error of any sort, just an extra “feature” of LabVIEW.

Whew. Let me know if you run into trouble or have questions!

Makefile.txt (2.71 KB)


Makefile.txt (2.71 KB)

If all that seems daunting, feel free to describe the higher order stuff that you are finding difficult to do in LV. Pretty sure I can help if you’d rather go that direction.

The other things I’ll mention about hooking C code to LV is that you need to watch carefully what the parameter types are. It is obvious, but since LV has to trust what you tell it about the types, it is rather blind. If you don’t get the number of dereferences correct, either the C code will crash, or LV will crash when it returns to find its memory corrupted. Also, be careful about ownership of buffers. It is possible for the DLL to reallocate the LV arrays, but only if you know the implementation of the LV arrays. Don’t assume you can just realloc them, or assume they are vectors, they aren’t. It is far better to keep the allocation and resizing and cleanup of a piece in the same component. So, allocate, resize, and free on the diagram only letting the C code read the data, OR do all of these in C and make LV treat the buffer as an opaque type.

If you need knowledge of LV types, alignment, etc, look for the appendix that goes into those details, and of course ask lots of questions.

Greg McKaskle

Well, to be honest, after struggling to figure all of this out, we had already gotten our difficult LabVIEW code working. My experience is right now that it’s best just to stick with one language, but I wanted to share my discovery of how to combine the languages in case someone out there would find benefit.

I’m glad to hear you aren’t stuck, and I’m glad you shared the details. I’ll do what I can to help either route you pick.

Having gone through the integration many times, I also agree that the single language route is way easier in so many ways. With a good enough library, it can be worth the effort, but I’ve spent weeks getting a library hooked up before. It certainly doesn’t seem like the sort of thing to tackle during a build season.

None of my computers are even installed to do attempt the hybrid at this point.

Greg McKaskle