LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Can I successfully use CVI DAQmx functionality in managed C++?

I ran into a really oddball problem where I attempted to import CVI DAQmx "C" functionality into C# using Visual Studio 2005 and .NET 2.0. What happened was that in my everyNsamples callback that was implemented in C# using the "unsafe" keyword, I would call a function to read data from a PCI-6111 card. Quite randomly on a specific hardware platform, I would receive a data set full of 0's without the function returning any errors.

I managed to run two CVI samples on the same hardware platform that did not show the error. After working with an NI AE, the best conclusion that we were able to come to was that the call to get the data was always properly passing the data between C and C#.

I need to be able to easily access this functionality from C#, and I have started a test project in Visual Studio 2005 using managed C++. I have have been successful at compiling a managed C++ project with that calls a DAQmx function. So, without yet having run the project, I am wondering whether anyone has successfully used DAQmx "C" functions in a managed C++ project, and if so, whether there are any KB articles on doing this.

Of course, this post may be moot as I will probably run the project tomorrow and answer my own questions, but I figure why not ask.

Thanks in advance!

Best Regards,
Matthew

0 Kudos
Message 1 of 11
(5,161 Views)
Hey Matthew,

Is there any particular reason you are importing C code from CVI into a .NET environment?  Since the C code is unmanaged and C# is managed you're likely to run into a variety of problems.  This is why National Instruments actually has different APIs for C and C#.  If you're just starting out with C# I'd recommend building off of the various examples we include with the driver. 

Measurement Studio is one of our products that makes programming with C# much easier.  In addition to assistants that help you setup your DAQmx tasks, it includes advanced UI for test and measurement and ships with a number of C# examples that you can find here: C:\Program Files\National Instruments\MeasurementStudioVS2005\DotNET\Examples\DAQmx

If you don't have Measurement Studio installed, but rather just the DAQmx driver, you'll still have a Measurement Studio directory in your National Instruments directory that contains examples.  However, they will not have any UI.

I'm hoping this gives you more information off of which to build your application.  Importing C code from CVI into a .NET environment seems like it would cause a variety of issues.  Be sure to post back and let us know what you find out!
Elijah Kerry
NI Director, Software Community
Message 2 of 11
(5,145 Views)

Hi Matthew,

I just wanted to add on a few things that Elijah mentioned. As Elijah mentioned, we do have native .NET assemblies (including DAQmx) that were designed for the .NET environment.  You may already know this but support managed C++ in a little different way that we do C# and VB.NET.  I could repeat everything that this post says but since it goes into great detail on what our stance is, there is no need. The summary of that post is that we only support managed C++ through our .NET class libraries (since they are CLS compliant). This means that you should be able to use the DAQmx assembly in managed C++, however, we don't provide examples, customer support, documentation, etc for that. You can get this assembly free by simply downloading the DAQmx driver here.

I also thought it might be important to mention that we do offer MFC Visual C++ NI-DAQmx class libraries if you decided to use MFC instead. These class libraries however require Measurement Studio.

I'm glad that you did post and mention that you are using managed C++ as we are always monitoring to see what type of usage managed C++ is getting.  So far, we have had very few requests on adding support for managed C++. In any sense, I went ahead and added your usage case to our R&D list. If you would like, you could submit your thoughts about managed C++ to me via a product suggestion. 

If any of this isn't clear, just let me know.

Best Regards,

Jonathan N.
National Instruments
0 Kudos
Message 3 of 11
(5,152 Views)
Thanks to both of you for the replies.

I'll post here what I e-mailed to a NI support engineer about the C# DAQmx.NET functionality and why I have decided to go the managed C++ route.

Begin included text -- Sometimes, we have to change the input channels in a task. Within the Measurement Studio .NET classes, it appears that once you create a voltage channel, it is not possible to remove that channel from the task unless you get rid of the task. The only way that I have found to do that is to call "Task.Dispose()". It appears that if "Task.Dispose()" is called many times, our application slows way down. It is as if the resources allocated to the task are not being properly deallocated.

I specifically tested to see if commenting out the call to Task.Dispose() when we do a configuration change made a difference, and it definitely does. If I do not call Task.Dispose(), the system continues to properly. However, if the task is not deleted, I see no way to change the channels that are in a task. -- end included text

That said, I have done a quick test application where I built a managed C++ library and integrated the DAQmx CVI calls into the code. The code compiles and links properly, and seems to run fine as far as I have gotten.

The only thing that is "complicated" is registering a callback through the DAQmxRegisterEveryNSamplesEvent function call. However, I found this link which details a relatively straight forward approach to registering a managed function as a call back for unmanaged code.

I had attempted the same thing in C#, and found an approach that worked for doing it. However, I encountered a situation where the code would not work on one particular hardware platform. We are using a SuperMicro PDSBA+ motherboard with a Core 2 Duo E6600 processor. Strange as it may seem, the code would not operate properly on this particular hardware platform.

The symptoms of the problem were that each time the EveryNSamples callback would occur, I would call DAQmxReadBinaryI16 within the body of the callback to get the collected data, and I would randomly get 0 as a return from the function - indicating that no errors had occurred, the data array would return filled with 0's, and the "sampsPerChanRead" parameter was returned as 0.

This code runs correctly on two other hardware platforms, one an AMD Athlon64 FX-60 based system, and the other a Pentium 4 based system. To restate, we are using a NI PCI-6111 DAQ card.

Note, I was also able to alter two of the CVI sample applications (an analog in sample and an analog out sample) to run simultaneously and closely approximate our system's normal mode of operation (the sample applications were running in a mode where we saw the problem in the C#/CVI code). On the PDSBA+ based system, I did not see the same problem that I saw in the mixed C#/CVI application - thus ruling out a problem with the CVI codebase on that hardware platform.

I figure that on the hardware platform where the problem does occur, there is some sort of low-level incompatiblity between the C# code and the CVI code that causes the data not to be passed correctly across the boundary. It is the only explanation that I can come up with. That said, the C# code was considerably more "ugly" than what I have prototyped for the C++ managed code, and it is my bet that the problem will not occur with a managed C++ approach since the two languages are much more compatible than unmanaged C and C#.

One other reason for using managed C++ is compatibility with the existing C#.NET codebase we have. Using MFC based C++ could be a "nightmare" in terms of interoperability with our existing .NET codebase. I've been down the route of interoperating COM code with .NET code in that I wrote some .NET code to interoperate with VB 6, and there is much more work involved in interoperating the two than there is in writing managed C++ to interface with other .NET languages.

Anyway, these are the details of what I have been through and why I'm going the route of using CVI "C" functions in managed C++.

Best Regards,
Matthew

0 Kudos
Message 4 of 11
(5,139 Views)
One thing that I want to clarify is that our code does not call Task.Dispose() repeatedly on the same task. Our C# configuration function checks for the existence of a previous task. If it find one, it calls Task.Dispose() and then creates a new task. Repeatedly going through this process of disposing of an existing task and creating a new one several times is what eventually seems to cause our system to slow down as if resources are not properly deallocated.

Best Regards,
Matthew

0 Kudos
Message 5 of 11
(5,136 Views)
Hi Matthew,

I do appreciate you giving me a very detailed response on why you are using managed C++ as it will help us in the future when looking into what type of support we might provide for managed C++. One final question that I had was if most people in your company use managed C++ or do they use the other .NET languages?

Thanks
Jonathan N.
National Instruments
0 Kudos
Message 6 of 11
(5,106 Views)
Hi Jonathan,

We are primarily using C#. There is one other developer who is working with us at this time, he is a contract worker.

When I started here last year, I would have preferred to work in C++. However, the NI C++ libraries that were available at the time did not work with Visual Studio 2005. Thus, I picked C# since it was the closest to C++.

As I am working in C++ to build this component, I am noting that it still contains all the flexiblity that I remember.

Note that I received an answer from a NI support engineer to the problem that I was having with the NI .NET library, namely, the system slowing down if .Dispose() is called multiple times on a task. It appears that it is a known problem. On searching for the article on the NI site, I note that I am presently unable to find it. However, the article title is
"DAQmx Acquisition in Visual Studio .NET Slows Down When Creating/Disposing/Recreating DAQ Tasks"

Best Regards,
Matthew

0 Kudos
Message 7 of 11
(5,082 Views)
I thought you might appreciate a progress report on this. I have been highly successful.

First let me state that I did not need to use CVI calls. I only used DAQmx calls. Specifically, I linked to the DAQmx ANSI C library.

In the process, though, I enountered a couple of problems.

The first problem was with the methodology in link that I gave to the Microsoft site that specifies a means to call managed C++ functions as callbacks from unmanaged code. Using the technique in the article, I specified a callback that was a non-static member function of the C++ class I created to communicate with DAQmx. I then registered the callback with the function DAQmxRegisterEveryNSamplesEvent. The callback was successfully called, however, when the callback function returned, I would repeatedly get a Win32 exception. I believe that the source of the exception was the DAQmx code.

What I did to get around this particular problem was to write a C function that would then call my class' EveryNSamplesEvent handler function. For the C function to call the class event handler, I used one of the methodologies in the Microsoft article I referenced. Specifically, I made the function pointer that was used to call the class' event handler a global static variable.

The vast majority of the time, this methodolgy works. However, when we change settings, I explicity call DAQmxClearTask and recreate the task. I was unable to find another way to re-register a callback function with DAQmxRegisterEveryNSamplesEvent to the existing task other than to call "ClearTask" and recreate the task from scracth. (Although, I found methods that will change things like the number of channels in the task, the buffer size, etc.) I need to be able to specify a different number of samples that would trigger the EveryNSampes event.

On doing this and then calling StartTask, sometimes, the EveryNSamples event would not fire. I explicity traced the call sequence, and it stopped at "StartTask" with the EveryNSamples event then not firing. This does not occur every time; rather, it occurs randomly. However, it is unacceptable for our application.

So, the solution that I have implemented that seems to be working as we would like it to is to not use the EveryNSamples event. Rather, I spawn a thread that calls DAQmxReadBinaryI16, StopTask, and StartTask in a loop that is controlled by a loop variable so that the loop will exit when I want it to.

The question of "Supporting" this type of useage was brought up earlier in the thread. What I'll say is this. It appears that the support for this type of scenario is more on Microsoft's Managed C++ environment than it is on the NI end of things. That is, even though I had a few issues that I had to work through, pretty much there was nothing special I had to do in Managed C++ for this methodology to just work. That said, the NI DAQmx C library (other than what I outlined above) easily integrated into Microsoft's Managed C++ code. Personally, I am quite impressed by this because my code is a mix of managed C++ and unmanaged C (within the same funcitons sometimes) with nothing to indicate to the complier which pieces are managed and which are unmanaged and it "just works."

Anyway, perhaps this post should have been in the Multifunction DAQ forum, however, I would expect that most any "C" libraries supplied by NI would integrate virtually as easily as my experience here perhaps with the exception of callback functions. I expect that anyone who chooses this approach would have to test their individual case - especially callbacks - to determine whether this approach is appropriate for them.

On that note, this approach performs better than the DAQmx.NET class library; that is, we are able to collect more sample sets per second using this approach. I did not make a comparison of performance with the  DAQmx MFC libraries. I shied away from the DAQmx MFC libraries because I had had problems with what appears to be the way threading is implemented in the DAQmx.NET libraries, and I saw that the DAQmx MFC libraries implemented their own thread management, too. IMHO, the way threading is handled in the DAQmx.NET libraries does not conform to Microsoft convention, and I did not want to take the chance that a similar problem would exist in the DAQmx MFC libraries.

Best Regards,
Matthew

0 Kudos
Message 8 of 11
(5,004 Views)
We appreciate the update Matt!  Smiley Happy
Elijah Kerry
NI Director, Software Community
Message 9 of 11
(4,980 Views)
Matthew,

It sounds like you might already be happy with your loop in a separate thread, but you should be able to change N for your Every-N-Samples event without clearing and recreating your task.  You can call DAQmxRegisterEveryNSamplesEvent with NULL as the callback function pointer to 'unregister' your original event and then call it again with your valid callback function pointer and a new value of N.  You might even be able to skip the call with NULL and directly call DAQmxRegisterEveryNSamplesEvent again with the new value of N to replace the old event with the new one, but I'm not sure about that.  The second method might return an error- I'm not sure as I can't test it right now.

-Jeff

Message 10 of 11
(4,951 Views)