From 04:00 PM CDT – 08:00 PM CDT (09:00 PM UTC – 01:00 AM UTC) Tuesday, April 16, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

return c++ .net parameters to invoke_node

Solved!
Go to solution

I have a c++ dll (unmanaged, non-.Net) with over 80 routines that can be successfully called from LabVIEW using the Call Library Function Nodes.

 

I am trying to create a new .Net version that can be called from the Constructor / Invoke Node Dlls. So far, I took two of the simple routines, created a .Net dll assembly, can connect to my class in the assembly with the LabVIEW Constructor, and can call the routines using Invoke Node.

 

But, all the connectors for the parameters (other than the return value) on the InvokeNode connector are inputs. What am I missing (in C++ or the InvokeNode implementation) to get the method parameters to be output connectors?

 

typical declaration:

unsigned int __clrcall Class1::Function1(int p1, int P2, char* p3)

{

}

 

The function value returns correctly, but I also need to return the P2 value back to LabVIEW.

 

 

Thanks,

RBM

0 Kudos
Message 1 of 10
(3,153 Views)

@brimcd wrote:

I have a c++ dll (unmanaged, non-.Net) with over 80 routines that can be successfully called from LabVIEW using the Call Library Function Nodes.

 

I am trying to create a new .Net version that can be called from the Constructor / Invoke Node Dlls. So far, I took two of the simple routines, created a .Net dll assembly, can connect to my class in the assembly with the LabVIEW Constructor, and can call the routines using Invoke Node.

 

But, all the connectors for the parameters (other than the return value) on the InvokeNode connector are inputs. What am I missing (in C++ or the InvokeNode implementation) to get the method parameters to be output connectors?

 

typical declaration:

unsigned int __clrcall Class1::Function1(int p1, int P2, char* p3)

{

}

 

The function value returns correctly, but I also need to return the P2 value back to LabVIEW.

 

 

Thanks,

RBM


How would you do that? P2 is passed by value and no magic in the world will allow you to modify its value in the function in such a way that the caller can see that change. You will at least have to declare the function as:

 

unsigned int __clrcall Class1::Function1(int p1, int *P2, char* p3);

 

and most likely also define somewhere in the project how the .Net interface should be created. I have no knowledge about creating .Net assemblies from C++, as I only have used C# for that, which takes automatically care about a lot of this. But generally I do not even bother with .Net as I find a classic DLL interface from C(++) much leaner and cleaner.

Rolf Kalbermatter
My Blog
0 Kudos
Message 2 of 10
(3,119 Views)

You're right, of course. After many attempts, labview forum searches, and google (even Bing) searches, I was trying anything - even the obviously wrong - to see if it would change the behavior of the invoke node.vi, and I ended up posting a poor selection of the implementation. I had tried int*, out, ref and a few other absurd things. Nothing changed the invoke node terminals. I thought there was a better chance of finding the answer here than a c++ forum.

 

I was hoping someone could post a short piece of code that invoke node will set an output terminal. Or is this just not possible from c++, and I should stick with CallLibraryFunction?

 

 

Thanks,

rbm

0 Kudos
Message 3 of 10
(3,106 Views)

I'm sure it is possible, but there is most likely some special keyword you have to add to the according parameter, that for the C++ compiler is defined as an empty statement but for the .Net assembly builder/linker will generate the correct typelibrary definition.

Rolf Kalbermatter
My Blog
0 Kudos
Message 4 of 10
(3,101 Views)

Looks like there's a weird syntax for this, does this link help? http://msdn.microsoft.com/en-us/library/h9t2463y.aspx

0 Kudos
Message 5 of 10
(3,090 Views)

Great idea - thanks. I tried it 2 ways:

 

unsigned int __clrcall CTestClass::TestInOut(int iBitRate, [Runtime::InteropServices::Out] int *iError, char* sError)

 

and, the Visual Studio 2013 intellisense suggested:

 

unsigned int __clrcall CTestClass::TestInOut(int iBitRate, [Runtime::InteropServices::OutAttribute] int *iError, char* sError)

 

compiles and linked correctly, and I created a new method name to make sure I was loading the new assembly (been burned on that one before).

 

but the results in the block diagram (attached .png) for the iError connector are still input only.

 

Thanks,

Brian

 

0 Kudos
Message 6 of 10
(3,085 Views)
Solution
Accepted by topic author brimcd

In addition to the attribute, looks like you might need to replace the * with ^ or %? I don't understand the syntax here, though - you might have to work through it.

Message 7 of 10
(3,079 Views)

 

Wow - THANKS!

I've never used the % before, and didn't (still don't) know what it is. It takes a bit of work to google the appropriate definition - a 'Tracking Reference"

 

That was the magic that changedthings:

  The int*% to an output

  The char*% to an input AND output.

 

Of course, be careful what you wish for. The int*% returned as an int32 with no issues, but trying to use the returned .Net Refnum from the char*% connector in Labview results in a specatcular crash. It disappears, I need program manager to kill the Labview process, and it will not recover the vi (Start LV, select recover, blank front panel appears and then LV disappears again). repeat as needed. Full reboot seems necessary.

 

BUT - I have something to start with now!

KUDOs!

 

Brian

 

0 Kudos
Message 8 of 10
(3,074 Views)

@brimcd wrote:

Of course, be careful what you wish for. The int*% returned as an int32 with no issues, but trying to use the returned .Net Refnum from the char*% connector in Labview results in a specatcular crash. It disappears, I need program manager to kill the Labview process, and it will not recover the vi (Start LV, select recover, blank front panel appears and then LV disappears again). repeat as needed. Full reboot seems necessary.


Glad to hear you've got something to work with. I'm not at all surprised you crashed LabVIEW; as soon as you start passing around pointers that LabVIEW allocated to outside libraries, you're likely to run into some problems. LabVIEW stores strings internally as Pascal-style strings (with the length) as opposed to C which uses null-terminated strings. When you pass a string from LabVIEW to an external library, I believe it makes a copy of that string into a new C string and null terminates it, then passes that to the library. If you then do something in your code that changes the memory allocation (for example resizing the string, or attempting to point at a different string) I wouldn't be surprised if it crashes, although I don't know how to fix it.

 

It also appears that you should change "* %" to "^ %". As far as I can tell, ^ is to % as * is to & (corresponding reference and dereference operators) and you shouldn't mix them to avoid reference-counting problems. EDIT: ignore that, it is apparently not as simple as I thought, which one to use is somehow also tied into whether it's a native data type.

0 Kudos
Message 9 of 10
(3,066 Views)

@nathand wrote:

@brimcd wrote:

Of course, be careful what you wish for. The int*% returned as an int32 with no issues, but trying to use the returned .Net Refnum from the char*% connector in Labview results in a specatcular crash. It disappears, I need program manager to kill the Labview process, and it will not recover the vi (Start LV, select recover, blank front panel appears and then LV disappears again). repeat as needed. Full reboot seems necessary.


Glad to hear you've got something to work with. I'm not at all surprised you crashed LabVIEW; as soon as you start passing around pointers that LabVIEW allocated to outside libraries, you're likely to run into some problems. LabVIEW stores strings internally as Pascal-style strings (with the length) as opposed to C which uses null-terminated strings. When you pass a string from LabVIEW to an external library, I believe it makes a copy of that string into a new C string and null terminates it, then passes that to the library. If you then do something in your code that changes the memory allocation (for example resizing the string, or attempting to point at a different string) I wouldn't be surprised if it crashes, although I don't know how to fix it.

 

It also appears that you should change "* %" to "^ %". As far as I can tell, ^ is to % as * is to & (corresponding reference and dereference operators) and you shouldn't mix them to avoid reference-counting problems. EDIT: ignore that, it is apparently not as simple as I thought, which one to use is somehow also tied into whether it's a native data type.


Actually .Net is distinctly different in how data is passed between LabVIEW and the .Net environment since .Net is itself a managed environment with very strict, but quite different data type definitions. LabVIEW tries to do the according conversion here. For strings this is typically either a String Variant containing either a WideChar or BStr or directly a BStr. .Net isn't always very consistent here. The type library definition should however give LabVIEW enough information to create the right translation.

 

When passing LabVIEW Strings (which are really Long Pascal Strings rather than Pascal Strings Smiley Wink) to external C code through the Call Library Node, LabVIEW doesn't really always allocate a new string copying its own string into it, but rather resizes the underlaying handle and adding a NULL termination byte and then passing the pointer to the string data to the external code. This can result in the memory manager creating a new memory buffer and copying the original content into it if the old handle can not be resized inplace but doesn't have to. The advantage of this also is that LabVIEW does not have to deallocate a temporary buffer after the Call Library Node returns as it can continue to use the same buffer throughout the rest of the diagram. The additional NULL byte at the end of the string doesn't hurt in any way, LabVIEW only uses the inherent string size to deal with the actual string data length.

Rolf Kalbermatter
My Blog
Message 10 of 10
(3,044 Views)