LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Passing a Windows Form reference from C# .NET to LabVIEW

Solved!
Go to solution

Hi folks,

I'm trying to pass a Windows Form reference (of an already launched Form/i.e. a GUI for those non C# programmers !) from C# .NET to LabVIEW so I can call a public function (called Update_Status_of_LIM) declared in that Form's class.

 

Attached is the vi receiving the reference and below are the relevant bits of C# .NET code  When C# calls up the VI's SetControlValue method, the Variant to Data primitive in that VI gives error 91 when it tries converting it to the correct reference type.  There are some additional comments on the VI's BD showing how I can get it to work if I instantiate the Form using a .NET constructor in LV, but this creates a 2nd instance of the Form which I don't want.

 

Am I going about this the wrong way ?

 

 

namespace LungInterface_v0._1
{
    public partial class LungInterfaceModule : Form
    {

        public void Update_Status_of_LIM(string Status_of_LIM)
        {
            this.Status_of_LIM.Text = Status_of_LIM;
        }
        private void PassLIMsFormAndHandleToLabVIEW()
        {
            Form LIMsForm = LungInterfaceModule.ActiveForm;
            ApplicationClass AppClassObjInstance = new ApplicationClass();
            VirtualInstrument object_VI = AppClassObjInstance.GetVIReference("LIMsReferences v0.2.vi", 0, 0, 0);
            object_VI.SetControlValue("LIMsReferenceIn", LIMsForm);
        }

   }

}

 

 

rgds

Peter
Download All
0 Kudos
Message 1 of 14
(11,219 Views)

I have cross posted this to Info-LabVIEW and am summarising progress to both threads.

 

On 20 July 2010 02:08, Adam Kemp <Adam.Kemp@ni.com> wrote:

Have you tried using a regular .Net refnum control instead of a variant?
If that doesn't work then try using the variant utility VIs
(vi.lib\Utility\VariantDataType) to query the actual type passed in to the
variant. See if it matches what you expect. Use GetTypeInfo.vi to find the
basic type and then if that's what you expect (it should be Refnum) then
use GetRefnumInfo.vi to verify that it's a DotNet refnum. Sometimes you
can end up with weird situations where instead of a variant containing X
you get a variant containing a variant containing X. It helps to know what
is actually being put in the variant so you know how to pull it out
correctly.
--
Adam Kemp
adam.kemp@ni.com
(512) 683-6058
Peter
0 Kudos
Message 2 of 14
(11,144 Views)

Hi Adam,
Thanks for the suggestions.  I tried them all out but unfortunately I don't think I am any closer to solving the problem.


>Have you tried using a regular .Net refnum control instead of a variant?

Yep, unfortunately doing that raises an exception in the .NET dev environment when I try to set the control with the reference value at the line:

        object_VI.SetControlValue("LIMsReferenceIn", LIMsForm);


Exception details:

       [System.Runtime.InteropServices.COMException]
            {"Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))"}



>
If that doesn't work then try using the variant utility VIs (vi.lib\Utility\VariantDataType) to query the actual type passed in to the variant.
>Use GetTypeInfo.vi to find the basic type
This returns the type "External Data" which is rather non descriptive.
>and then if that's what you expect (it should be Refnum) then  use GetRefnumInfo.vi to verify that it's a DotNet refnum.

 

This vi gives an error because the type isn't a refnum.

 

rgds

Peter
0 Kudos
Message 3 of 14
(11,141 Views)

Hi Adam, thanks again for the suggestion.  I tried building a .NET interop Assembly in LabVIEW (that part worked),  I then referenced that interop assembly in my C# project, but C# wasn't happy trying to convert the Form reference into a LabVIEW reference type (LVReferenceNumber).  The following errors ensued before I could even build the C# assembly:

Error    1    The best overloaded method match for 'ASL5000_InteropAssembly.ASL5000_LabVIEWExports.LIMsReferencesv03(NationalInstruments.LabVIEW.Interop.LVReferenceNumber)' has some invalid arguments
Error    2    Argument '1': cannot convert from 'System.Windows.Forms.Form' to 'NationalInstruments.LabVIEW.Interop.LVReferenceNumber'   


The errors are caused by the last line of the following code:

 

   private void PassLIMsFormAndHandleToLabVIEW()
   {
      Form LIMsForm = LungInterfaceModule.ActiveForm;
     ASL5000_InteropAssembly.ASL5000_LabVIEWExports.LIMsReferencesv03(LIMsForm);
   }

On 21 July 2010 01:58, Adam Kemp <Adam.Kemp@ni.com> wrote:
>
> Another LabVIEW developer suggested that you try to use a .Net Interop
> Assembly build specification to create a .Net assembly from your VI which
> you can call directly from .Net. Try using the .Net refnum type for your
> control again and make that one of the inputs to your function. Then you
> can just call a function and pass in the .Net object like a normal .Net
> function.
> --
> Adam Kemp
> adam.kemp@ni.com
> (512) 683-6058

Peter
0 Kudos
Message 4 of 14
(11,116 Views)

Thanks again Adam.


>can you elaborate a bit more on what you're trying to do?

 

I have a .NET Assembly (a windows form) which is responsible for controlling a LabVIEW application.  I do this primarily by calling ActiveX methods of the LabVIEW ActiveX EXE.  I only have one instance of this .NET windows form.

The problem I am trying to solve is to be able to pass status information (in the form of strings) from LabVIEW back to some indicators/labels on the .NET GUI (form).   Now I could get the .NET assembly to routinely poll this status information by regularly making an ActiveX call back to the EXE, but I am against polling in general and therefore want to pass the data back to the .NET assembly only when the status data changes.   As I wrote previously, in LabVIEW land I can indeed use a .NET constructor to open a new form and call a method in the form's class to pass this status string back to .NET land - BUT doing it that way creates a new instance of the form.

You ask about sending signals of some sort to the C# code to achieve this.  That is effectively what I just described above and it only works by instantiating a new instance of the form from LabVIEW - hence my claimed need to get a reference to the original form.

Any thoughts of using a .NET callback/event in LabVIEW once again instantiates a NEW form, rather than using the existing form.

On 22 July 2010 08:49, Adam Kemp <Adam.Kemp@ni.com> wrote:
Peter, I've asked another LabVIEW developer to look into this a bit more
to see if we can find a solution for you. I'll let you know when I have
more information.

In the meantime, can you elaborate a bit more on what you're trying to do?
What does the G code need to do with the .Net reference, and why can't
that method just be called from the C# code instead? Have you considered
just sending signals of some sort to the C# code to do the stuff that
needs this reference?
--
Adam Kemp
adam.kemp@ni.com
(512) 683-6058

 

regards

Peter
0 Kudos
Message 5 of 14
(11,097 Views)

Thanks for that suggested solution Adam,

I will wait until you hear back from the owner of the .Net assembly build before pursuing what you suggested in case his solution is much simpler (I can only hope !)
While what you pose will most likely work, I'm still wondering why there isn't an easier way (along the lines I was initially trying). 


On 23 July 2010 07:51, Adam Kemp <Adam.Kemp@ni.com> wrote:
I still haven't gotten an answer from the owner of the .Net assembly build
spec feature, but I did manage to find a method that works. What you need
to do is pass the window handle for the form that you want to signal
(there's a Handle property in the Form class) into LabVIEW. Then from
LabVIEW you can call PostMessage in user32.dll (using a Call Library Node)
to send a user event (WM_USER) to that window. To handle the event you
just have to override the WndProc method in your window's Form class.
There's an example code snippet for that here:
http://www.devsource.com/c/a/Using-VS/Working-with-Windows-Messages-in-NET/2/

If you need to get any data back from LabVIEW as a result of that signal
then it would probably be easier to call back into LabVIEW from the event
handler than to pass the data in with the window message (unless you just
need a couple numbers).

Let me know if you have trouble figuring out how to do what I just
described. I can clean up my test code and send it to you if you need an
example to look at.
--
Adam Kemp
adam.kemp@ni.com
(512) 683-6058

 

rgds

Peter
0 Kudos
Message 6 of 14
(11,081 Views)
Solution
Accepted by topic author Peter_B

Hi Adam,

In one sense I'm glad you decided to call this limitation a bug as it makes me feel less stupid !   OTOH the work arounds are klunky until it is fixed up.

I am much less experienced in C# c.f. LV,   but trying to solve this problem of passing an object from C# to LV,  and now being told it isn't possible has taught me a lot.

Could you please post the CAR number once you have registered this as a bug ?

Many thanks for following up on this.
regards

Peter Badcock
Product Development
ResMed Ltd


On 27 July 2010 02:47, Adam Kemp <Adam.Kemp@ni.com> wrote:
Apparently we just didn't implement the necessary marshalling code to
convert between a .Net object on the .Net side and a .Net refnum on the
LabVIEW side. In LabVIEW we refer to .Net objects with a refnum through
our common refnum manager which ensures that accessing objects is
threadsafe and that accesses to disposed objects will return an error
instead of throwing an exception or crashing. That refnum is really just a
32-bit number which has no meaning to .Net code. In order to actually use
the object on the .Net side we would need to pull out the real object
during marshalling. We didn't implement that, though, so you end up with
the useless number.

I can definitely see how limiting this is, so I'm going to create a bug
report for us to implement that. In the meantime I'm not sure how to get
an object from C# to LV.
--
Adam Kemp
 
From: Peter Badcock
Hi Adam,

Have you heard back from the owner of the .Net assembly build spec feature
regarding a simpler way to do what I am trying to do ?  Ask them if they
can see any technical reason (such as managed versus unmanaged code
spaces) why I can't just get a reference to the label on the .NET windows
form to update its contents.

rgds
Peter Badcock

 

Peter
0 Kudos
Message 7 of 14
(11,039 Views)

Many thanks Adam.


On 28 July 2010 07:42, Adam Kemp <Adam.Kemp@ni.com> wrote:
While looking into this issue I filed these CARs:

CAR# 242214: "LV-built .Net Assembly does not marshal .Net objects between
LV and .Net"
CAR# 242222: "LV-built .Net assembly throws exception when you try to run
.Net code in it" (not sure if you ran into this, but I did)
CAR# 242227: "LV-built .Net assembly function hangs if it opens a modal
dialog" (again, I don't think you hit this, but it was annoying)
CAR# 242320  "LV-built DLL function hangs when opening modal windows"
(workaround is to uncheck the "Delay operating system messages" option in
the advanced configuration settings for the build spec)

I think you only really hit the first one, but the other ones were bugs as
well.
--
Adam Kemp
adam.kemp@ni.com
(512) 683-6058
regards
Peter
0 Kudos
Message 8 of 14
(11,004 Views)

Hi Peter,

 

for all that I can read here, I'm still missing some info: is the .NET exe written by someone else, not you? Why an exe, why not a dll - has it to be started and run on its own, without LV?

When I want to show/update a .NET window from LV, I encapsulate the Windows.Forms namespace and form handling inside a dll and introduce public methods to show/update the window, similar to what you did. But this simple approach leads to having the .NET part in the same process, not in a process of its own. The latter makes the whole thing a little more complicated.

 

BTW: It's recommended to make .NET GUI control updates thread safe, when called from a different thread (e.g. an event handler), what a line like

 

this.Status_of_LIM.Text = Status_of_LIM;

 

isn't! For more info on this, see:

http://msdn.microsoft.com/en-us/library/ms171728%28VS.80%29.aspx

 

 

Cheers,

Hans

 

Message 9 of 14
(10,920 Views)

Hi Hans,

 

Thanks for your questions and thoughts.

 

The .NET exe is written by me.  It is a Windows Form application which is why it is an EXE rather than a dll.  It needs to be started and run on its own before the LabVIEW application is even launched.  So yes the .NET app needs to be in another process.

 

As a novice C# programmer, I wasn't aware of what makes certain code thread safe or not.  I don't get any error messages or warnings, so until I come across race conditions or deadlocks etc I will stick with the simplest method that works for me.

 

BTW I have implemented the temporary solution outlined by Adam by using the Windows messaging queue where a vi posts a user msg (WM_USER) to advise the .NET app that new data is available.   This works fine but needed additional coding/learning on my part in C#.

 

rgds

Peter
0 Kudos
Message 10 of 14
(10,910 Views)