Jump to content

Calling arbitrary code straight from the diagram


Recommended Posts

No this is definitely cool. It’s cool to know that there is a way to keep the data in the same scope. Now all you need is a (c compiler/assembler) written in labview along with prebuild actions and the world would be your oyster. 

Link to post
Share on other sites
11 hours ago, Taylorh140 said:

No this is definitely cool. It’s cool to know that there is a way to keep the data in the same scope. Now all you need is a (c compiler/assembler) written in labview along with prebuild actions and the world would be your oyster. 

That's a bit ambitious!¬†ūüėÄ

I would rather think something in the sense of the Python ctypes package to allow arbitrary function calls to DLLs including callbacks and such. We just need to find a method that does the opposite for this: calling a VI as C function pointer.¬†ūüėÄ

Link to post
Share on other sites
4 hours ago, Rolf Kalbermatter said:

We just need to find a method that does the opposite for this: calling a VI as C function pointer.¬†ūüėÄ

hmmmm. ;)

Edited by ShaunR
Link to post
Share on other sites
14 hours ago, ShaunR said:

hmmmm. ;)

No not really. I mean something quite different.

Given a VI create a sort of function wrapper around it that works as a C function pointer. For that we would need something like

MgErr CallVIFunc(VIDSRef viRef, int32 numInParams,  VIParams *inParams, int32 numOutParams, VIParams *outParams);

with both parameters something like an array of

typedef struct
{
  LStrHandle controlName;
  int16 *typedesc;
  void *data;
} VIParams;

That way one could do a C function wrapper in assembly code that then converts its C parameters into LabVIEW parameters and then calls the VI as function.

These are not actual functions that exist but just something I came up with. I'm sure something similar actually exists!

Edited by Rolf Kalbermatter
Link to post
Share on other sites
On 9/29/2020 at 12:54 AM, Rolf Kalbermatter said:

These are not actual functions that exist but just something I came up with. I'm sure something similar actually exists!

All I could find about this is just these two internal functions:

  • CallVIFromDll
  • NCGRunVirtualInstrument

For the first one I was able to find .NET prototype only:

CallVIFromDll.Invoke(Int32 epIndex, IntPtr lvClient, IntPtr entryPointDataSpace)

I'm kind of unsure, how it could be used for the mentioned task. It looks like it doesn't accept the VI parameters. And what do these arguments mean exactly?..

As to the second one, it doesn't accept the VI parameters as well and must be called in UI Thread only. The prototype is as follows:

int32_t NCGRunVirtualInstrument(uint32_t VIRef);

I did a limited testing and it appears to work. The VI is launched with the parameters on its FP and no panel is shown. We could prepare the parameters before the call with Control Value.Set method. Not very flexible solution, as I think.

 

I saw your post from 2016, where you said that you have found some functions suitable for the task. Do you remember the details?

Link to post
Share on other sites
7 hours ago, dadreamer said:

All I could find about this is just these two internal functions:

  • CallVIFromDll
  • NCGRunVirtualInstrument

For the first one I was able to find .NET prototype only:


CallVIFromDll.Invoke(Int32 epIndex, IntPtr lvClient, IntPtr entryPointDataSpace)

I'm kind of unsure, how it could be used for the mentioned task. It looks like it doesn't accept the VI parameters. And what do these arguments mean exactly?..

As to the second one, it doesn't accept the VI parameters as well and must be called in UI Thread only. The prototype is as follows:


int32_t NCGRunVirtualInstrument(uint32_t VIRef);

I did a limited testing and it appears to work. The VI is launched with the parameters on its FP and no panel is shown. We could prepare the parameters before the call with Control Value.Set method. Not very flexible solution, as I think.

 

I saw your post from 2016, where you said that you have found some functions suitable for the task. Do you remember the details?

What about VIRefPrepNativeCall and VIRefFinishNativeCall? They sound interesting but maybe a red herring.

Link to post
Share on other sites
16 hours ago, ShaunR said:

What about VIRefPrepNativeCall and VIRefFinishNativeCall? They sound interesting but maybe a red herring.

I'm afraid, we can't use them, because they don't actually run a VI, but "prepare" its state for a run. I guess it's used only for LabVIEW Adapters to be called later from TestStand. VIRefPrepNativeCall requires a VI to be in reserved state, otherwise it returns 1027 error. If we mark the VI as reserved with StatVIRefReserve, then it all goes OK, but the target VI is not executed. Something like this:

2020-10-01_14-29-48.jpg.f0c35e037c676e45c5dd3b3addb869e3.jpg

int32_t StatVIRefReserve(uintptr_t viDS, uint32_t *pVIRef, int32_t unknown, int32_t setUnset);
int32_t VIRefPrepNativeCall(uint32_t viRef, uintptr_t *pVIDS);
int32_t VIRefFinishNativeCall(uint32_t viRef);
void  StatVIRefRelease(uint32_t viRef);

There must be something between VIRefPrepNativeCall and VIRefFinishNativeCall like NCGRunVirtualInstrument, but with the ability to pass the parameters. Of course, we could use WriteDCOTransferData before the call to set our parameters, but the BD / assembly code becomes kinda cumbersome then.

Link to post
Share on other sites

I think CallInstrument() is more promising although the documenttion I found seems to indicate that it is an old function that is superseded by something called C Interface in LabVIEW. But I haven't found any information about that new interface.

/* Legacy C function access to call a VI. Newer code should consider upgrading to use the C Interface to LabVIEW. */

/*  Flags to influence window behavior when calling a VI synchronously via CallInstrument* functions.

	The following flags offer a refinement to how the former 'modal' input to
	CallInstrument* functions works. For compatibility, a value of TRUE still
	maps to a standard modal VI. Injecting the kCI_AppModalWindow flag will allow
	the VI to stack above any Dlg*-based (C-based LV) windows that may be open as well.
	Use kCI_AppModalWindow with caution! Dlg*-based dialogs run at root loop, and VIs
	that run as app modal windows might be subject to deadlocking the UI. */
const int32 kCI_DefaultWindow	= 0L;		///< in CallInstrument*, display VI's window using VI's default window styles
const int32 kCI_ModalWindow		= 1L<<0;	///< in CallInstrument*, display VI's window as modal
const int32 kCI_AppModalWindow	= 1L<<1;	///< in CallInstrument*, display VI's window as 'application modal'

/* Legacy C function access to call a VI. Newer code should consider upgrading to use the C Interface to LabVIEW. */
/*
	@param viPath fully qualified path to the VI
	@param windowFlags flags that influence how the VIs window will be shown
	@param nInputs number of input parameters to send to the VI
	@param nOutputs number of output parameters to read from the VI
	@return error code describing whether the VI call succeeded

	The actual parameters follow nOutputs and are specified by a combination of
	parameter Name (PStr), type (int16*) and data pointer.

	@example
		CallInstrument(vi, kCI_ModalWindow, 2, 1,
		               "\07Param 1", int16_Type_descriptor_in1, &p1,
		               "\07Param 2", int16_Type_descriptor_in2, &p2,
		               "\06Result", int16_Type_descriptor_res, &res);


	@note This function does not allow the caller to specify a LabVIEW context in which to load
	      and run the VI. Use a newer API (such as the C Interface to LV) to do so.

	@note Valid values for windowFlags are:
	        kCI_DefaultWindow  (0L)
	        kCI_ModalWindow    (1L<<0)
	        kCI_AppModalWindow (1L<<1)
*/
TH_SINGLE_UI EXTERNC MgErr _FUNCC CallInstrument(Path viPath, int32 windowFlags, int32 nInputs, int32 nOutputs, ...);

 

  • Thanks 1
Link to post
Share on other sites
Posted (edited)

Nice catch, Rolf! It works and I am able to pass the input/output parameters now.

2020-10-05_13-00-41.jpg.588b4620274cd690c90b033cd23dabb2.jpg

RunVI.vi

SubVI.vi

But it appears that CallInstrument wants to be ran in UI Thread only, else LabVIEW goes big crash. It calls the VI synchronously, waiting until it finishes. That makes me think that this function is not the best idea to use as a callback, because when the user will be interacting with some GUI elements or the program will be running some property/invoke nodes, the callback VI will be waiting UI Thread to become idle, thus we could experience the delay between the events from our callback (or even loss of earlier ones?). It would be much better to run the VI in any thread somehow, but CallInstrument doesn't support that. That's why I decided not to adapt the asm samples for that function for now. Maybe I would be lucky enough to find some other options or overcome the threading issues somehow. Or end on PostLVUserEvent until some better ideas come to mind. ūüôā

On 10/3/2020 at 8:52 PM, Rolf Kalbermatter said:

But I haven't found any information about that new interface.

It's likely has to do with these two functions: GetCIntVIServerFuncs and GetCInterfaceFunctionTable. They return some tables, filled with the function pointers. There are InitLVClient / InitLVClient2, UninitLVClient / UninitLVClientNoDelay, WaitLVClientReady, WaitLVShuttingDown and a whole bunch of unnamed functions (safety precautions?). It'll take a serious effort to study how those work.

Edited by dadreamer
added some thoughts
Link to post
Share on other sites
55 minutes ago, dadreamer said:

Nice catch, Rolf! It works and I am able to pass the input/output parameters now.

2020-10-05_13-00-41.jpg.588b4620274cd690c90b033cd23dabb2.jpg

RunVI.vi 10.95 kB · 0 downloads

SubVI.vi 12.43 kB · 0 downloads

But it appears that CallInstrument wants to be ran in UI Thread only, else LabVIEW goes big crash. It calls the VI synchronously, waiting until it finishes. That makes me think that this function is not the best idea to use as a callback, because when the user will be interacting with some GUI elements or the program will be running some property/invoke nodes, the callback VI will be waiting UI Thread to become idle, thus we could experience the delay between the events from our callback (or even loss of earlier ones?). It would be much better to run the VI in any thread somehow, but CallInstrument doesn't support that. That's why I decided not to adapt the asm samples for that function for now. Maybe I would be lucky enough to find some other options or overcome the threading issues somehow. Or end on PostLVUserEvent until some better ideas come to mind. ūüôā

I'm aware of the limitation with this function. It not only wants to run in the UI thread but also doesn't allow to specify the context it should be running under, so will likely always use the main application context (which could be fine really, as each project has its own context). And that with the missing context is logical since this function existed before LabVIEW 8.0 which introduced (at least publically) application contexts.

I think the mentioned C interface has to do with the function GetCInterfaceFunctionTable(), exported since around LabVIEW 8.0, but that is a big black hole. From what I could see, it returns a pointer to a function pointer table containing all kinds of functions. Some of them used to be exported as external functions in LabVIEW too. But without a header file declaring this structure and the actual functions it exports, it is totally hopeless to think one could use it. As most of the functions aren't really exported from LabVIEW in other ways they also didnt retain the function name, like all those other functions in the official export table. But even with a name it would be very tedious to find out what parameters a function takes and how to call it especially if it needs to be called in conjunction with other functions in there.

Edited by Rolf Kalbermatter
Link to post
Share on other sites
2 hours ago, dadreamer said:

There are InitLVClient / InitLVClient2, UninitLVClient / UninitLVClientNoDelay, WaitLVClientReady, WaitLVShuttingDown and a whole bunch of unnamed functions (safety precautions?). It'll take a serious effort to study how those work.

Not really safety precautions. Most C(++) compilers will strip by default all symbols from linked non-debug code, unless these symbols are needed for certain puposes like function export tables. While these functions also return a pointer to some sort of export table, they are not official export tables, just as virtual tables in C++ aren't really export tables. The name is unneeded as far as the compiler is concerned, so they get all stripped. This has certain anti reverse engineering reasons as someone distributing a release version isn't usually interested in letting its users reverse engineer their software¬† (just check your license agreement you entered into when installing LabVIEW¬†ūüėÄ) but the main reason is really that these symbols simply blow up the executable image size for no¬†useful¬†reason and it's an easy thing to do by the linker. The functions with retained symbol¬†names in there are usually functions that are also exported in the official export table.

GetCIntVIServerFuncs() existed before LabVIEW 8.0 and was/is mostly related to functions needed by various VI Server interfaces. The first version of VI server was a very small set of exported functions that got called by a CIN. This was then changed into the fully diagram accessible VI server interface as we know it now around LabVIEW 5. Sometimes the LabVIEW compiler needs to create callbacks into the LabVIEW kernel. Initially this was done through direct calls of exported functions but that was changed for several reasons to call a special internal export interface. Hiding things was likely more a byproduct than the main reason for this, making this interface more uniform among the different platforms was likely more important.

The C Interface supposedly took this idea further and here hiding LabVIEW internas might have been part of the decision. But because this privately exported function table is highly inflexible and can only be amended to in subsequent LabVIEW versions but never modified without creating serious trouble with version compatiblity, I think it's not something they would want to use for everything. The advantage is that you do not really need to use dynamic link functionality for this except for the one function to get at this interface, so there is one simple function that will use platform specific dynamic loading and everything else is simply a function pointer call into a table at a specific offset.

A COM like interface would be much more flexible in terms of version compatibility and NI uses that in some parts even across platforms despite that real COM isn't supported on non-Windows platforms, but it is also a lot more complicated to create and use even when you program everything in C++.

Edited by Rolf Kalbermatter
Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Similar Content

    • By CopperD
      Allows new functions to be compiled during runtime by using libtcc. http://bellard.org/tcc/
       
      Features Full ANSI C compiler  ISO C99 extensions (Missing only complex and imaginary numbers) GNU C extensions (See TCC Docs) TinyCC extensions (See TCC Docs) GNU-like inline assembler  32bit & 64bit opcodes depending on DLL (See TCC Docs) Compile to memory to call as function or disk as exe Allows for dynamic code Pointer safe checks  
      Examples
      Adding 2 numbers and using return to get the result
      Using system to call cmd
      Inline x86 assembly
      Passing in information using argc and argv
      Using pointers to pass a string in and an SHA512 hash out
       
       
      Uses libtcc unmodified, so for security reasons you can download the dll from the author's website or compile your own from source. The dll is included if you wish to use it.
       
      Some very basic examples that show off only a tiny subset of the features this compiler offers. Unless you are very careful, compiling functions during runtime can lead to unstable code. (Test before you deploy) All examples should run without issue but modification can and will lead to crashing. (Save often)
    • By CopperD
      Name: DCG
      Submitter: CopperD
      Submitted: 16 Feb 2016
      Category: *Uncertified*
      LabVIEW Version: Not ApplicableLicense Type: Creative Commons Attribution 3.0



      Allows new functions to be compiled during runtime by using libtcc. http://bellard.org/tcc/
       
      Features Full ANSI C compiler  ISO C99 extensions (Missing only complex and imaginary numbers) GNU C extensions (See TCC Docs) TinyCC extensions (See TCC Docs) GNU-like inline assembler  32bit & 64bit opcodes depending on DLL (See TCC Docs) Compile to memory to call as function or disk as exe Allows for dynamic code Pointer safe checks  
      Examples
      Adding 2 numbers and using return to get the result
      Using system to call cmd
      Inline x86 assembly
      Passing in information using argc and argv
      Using pointers to pass a string in and an SHA512 hash out
       
       
      Uses libtcc unmodified, so for security reasons you can download the dll from the author's website or compile your own from source. The dll is included if you wish to use it.
       
      Some very basic examples that show off only a tiny subset of the features this compiler offers. Unless you are very careful, compiling functions during runtime can lead to unstable code. (Test before you deploy) All examples should run without issue but modification can and will lead to crashing. (Save often)

      Click here to download this file
    • By Callan
      Hi all,
       
      I have been trying to use VIPM to distribute drivers that rely on a .NET framework that is unsigned (i.e not in the global assembly cache).  My issue is that after the package is installed, none of the VIs will work because LabVIEW cannot locate the .net assembly.  However, if I launch labview by opening the library VIPM installed, all of the VIs will work, and LabVIEW is able to locate the the .Net framework and it appears in the main application instance.  I need to be able to use the installed vis all the time, not just when LabVIEW is launched in this particular way.
       
      Here is a link to all the all the files I used: 
       
      https://www.dropbox.com/sh/yjt2a0t8msxhgfn/AAC4VVW-hPawwXtGMrZ6wuIra?dl=0
       
      Here are the steps I went through:
       
      1) We put all the .NET dlls and the LabVIEW.exe.config file in the Labview directory.
       

       
      2) We installed the package on our computer.  
       

       
      3)We closed everything and relaunched Labview from the start menu.  Then accessed the BioRobotics/Vicon palette.
       

       
      4)We placed the Get Ref subVI on the block diagram, opened it up, and ran it it.
       
      5) We got the following error:
       

       
      We also get a similar error for any VI that uses a method associated with the .NET dll.
       
      6) However, if we again start with everything closed and launch Labview by clicking on the VICON.lvlib located in the vi.lib
       

       
      7) Then repeat steps 3 and 4.  We do not get any errors in the VIs on the palette, and the .Net assembly loads fine.  We can even close the library and everything still works.  Somehow opening the library first makes LabVIEW know to load the ViconDataStreamSDK_DotNet assembly when using functions on the Vicon palette.
       

       
      If anyone does attempt to build a new package, it is worth noting that I included the dlls in the source files, and in the same folder as the vipb:
       

       
       
      Thanks!
       
      Callan
       
    • By rayjay
      I am trying to export a class in LabVIEW 2013 to a .NET Assembly.  According to this I can just select the members of the class that I want to export and it will generate a .NET class for them.  
       
      http://zone.ni.com/reference/en-XX/help/371361H-01/lvhowto/building_a_net_assembly/
       
      I am able to export the individual members of the class, but when I run the code in .NET it starts searching for mydllname.dll/myclassname.lvclass (the file of the class that the exported members belong to) and can't find it.  My next step was to add the class to the the Always Included list, but it seems that when I do that some of the members that I have in the Exported VI's list don't show up in the .NET assembly.  The class I'm trying to export is inhereted from another class, so I tried adding both classes to the Always Included list and all of the base class members to the Exported VI's list, but I still have members missing in the .NET classes.  Thinking it could be something releated to exporting LVOOP classes, I tried ditching the LVOOP class altogether and rewrote the labview code to use generic VI's and a cluser typedef to store the member data.  However, it seems like the labview builder doesn't recognize that the control and indicator clusers that I use to pass the member data belong to the same typedef, so it exports a different .NET class for each control and indicator cluster.  This creates a problem because I have no way of passing the output of one funtion to the next in .NET since I cannot convert between the two .NET types.
       
      Has anyone had problems like this/know how to fix it???  I couldn't find much online about building a .NET interop assembly beyond the basic configuration steps, so any help would be much appreciated!
       
      Thanks
    • By GregFreeman
      I am trying to open shell32.dll with .NET but an exception is being thrown. I'm seeing this type of exception all over when I search google but really not much related to LabVIEW (mostly vb.net, c#.net etc). I have tried loading shell32.dll from system32/shell32.dll and SysWOW64/shell32.dll and both fail. I am running 32 bit LabVIEW on Windows 7 64 bit. I'm guessing it has something to do with that. I am going to try on a coworkers 32 bit computer when I get a chance, but does anyone have suggestions?
      Thanks.
      Edit: Tried on 32 bit labview on a 32 bit machine and it still fails.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.