07-18-2010 11:28 PM
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
Solved! Go to Solution.
07-20-2010 07:03 AM
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
07-20-2010 07:09 AM
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.
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))"}
This vi gives an error because the type isn't a refnum.
rgds
07-20-2010 10:06 PM
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
07-22-2010 05:11 AM
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.
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?
regards
07-22-2010 08:56 PM
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).
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.
rgds
07-26-2010 06:55 PM
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
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 BadcockHi 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
07-27-2010 07:07 PM
Many thanks Adam.
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.
08-05-2010 02:49 AM - edited 08-05-2010 02:52 AM
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
08-06-2010 03:31 AM - edited 08-06-2010 03:33 AM
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