调用LabWindows™/ CVI™中以.NET语言编写的C#和其他DLL

更新 Jun 13, 2023

环境

软件

  • LabWindows/CVI

我使用LabWindows / CVI中的Loadlibrary函数根据指针直接调用C#编写的DLL中的函数,但失败了。然后,我尝试了创建DLL导入库文件的方法,但也失败了。那么,用C#编写的DLL和普通DLL有什么区别,以及如何在CVI中调用它?

C#编程规范中的DLL分为managed DLL和unmanaged DLL。Managed DLL是中间代码,并且完全依赖于.NET平台。Non-managed DLL是机器代码,并且不依赖于.NET平台。 C#,VB.NET和F#编程是用纯.NET语言开发的,并且生成的DLL属于Non-managed DLL。用其他语言(包括C / C ++,CVI和LabVIEW)编写的DLL是unmanaged DLL。Managed 应用程序和DLL可以直接调用Non-managed DLL,而Non-managed应用程序和DLL必须采用.NET Runtime来调用managed DLL。

CVI中调用DLL的方法分为两种方法:“显示调用”和“隐式调用”,这两种方法都适用于Non-managed DLL,即用.NET编写的DLL。有关详细信息,请参阅《在LabWindows / CVI中使用动态链接库的技术指南》。 display调用方法不需要被调用DLL的头文件, 而是使用windows.h提供的Loadlibrary , GetProcAddress和Freelibrary函数根据指针直接访问DLL中的函数。此方法适用于不带头文件的DLL,但适用于被调用函数的原型。隐式调用方法需要名为DLL的头文件,该头文件由创建DLL导入库.lib文件的方法调用。此方法使用“选项”选项卡中的“生成DLL导入库”向导来创建DLL导入库。将导入库添加到项目后,可以通过函数名称轻松灵活地使用被调用DLL中的函数。注意,要调用的函数需要在头文件中声明。

CVI调用C#和其他.NET语言DLL需要使用“ 工具”选项卡中的“ 创建.NET”控制器 。它生成一个包装程序,该包装程序调用.NET汇编代码,其中包含相应的仪器驱动程序,源文件和头文件。包装器生成的具体步骤如下:

1.从“ 工具”选项卡中选择“ 创建.NET控制器 ”。

2.通常,第三方开发的DLL不在全局程序集缓存中,因此请在弹出对话框中选中“ 按路径指定程序集”,然后选择要调用的C#DLL。

3.在目标仪器中指定一个仪器驱动程序.fp文件,然后单击确定 。 CVI程序将生成一个仪器驱动程序,可以调用所选的C#DLL。
4.将生成的.fp文件添加到项目中;


现在准备工作已经完成,让我们看一下.NET DLL编程方法和过程。包装程序自动生成的仪器驱动程序将封装一些调用函数。该函数的命名方法为[namespace] _ [class name] 。命名空间和类名称是在编写.NET程序时定义的,并且命名空间也是DLL的名称。如下所示, ClassLibrary1是一个名称空间, Class1是一个类名称,一个DLL包含一个名称空间,一个名称空间可以包含一个或多个类,一个类可以包含多个功能。 ClassLibrary1_Class1__add是DLL中的附加函数。
通常, Initialize_ [namespace]和Close_ [namespace]之 类的函数分别调用CDotNetLoadAssembly函数和CDotNetDiscardAssemblyHandle函数, [namespace] _ [class name] __ Create调用CDotNetCreateGenericInstance函数, [namespace] _ [class name] _ [Function name]调用CDotNetInvokeGenericStaticMember函数等等。

特别是,由于被调用的DLL不在全局程序集缓存中,因此您需要在调用前向CDotNetRegisterAssemblyPath函数注册.NET DLL。如果未注册,将显示如下所示的失败错误。

另外,C#编程不需要关心垃圾内存收集(Garbage Collector),并且C环境中被调用函数所涉及的变量需要通过手动调用释放内存的函数来释放变量的内存空间。编程的主要过程如下:

1.声明[DLL名称] _ [类名称]类型的句柄;
2.调用CDotNetRegisterAssemblyPath("[DLL name], Version=xxxx, Culture=xx, PublicKeyToken=xx", ''Full Path of DLL") 用于注册.NET DLL。DLL的路径分隔符使用“ \\” ,例如D:\\ CVI \\ Projects \\ C#net DLL调用 ;
3.调用Initialize_ [DLL name]函数来初始化.NET控制器;
4.根据句柄,调用[DLL name] _ [class name] __ Create以创建被调用DLL的实例;
5.调用特定的函数,例如[DLL name] _ [class name] _ [function name]并编写相应的代码;
6.调用CDotNetDiscardHandle释放.NET DLL实例句柄。
7.调用CDotNetFreeMemory释放变量内存;
8.调用Close_ [DLL name]卸载.NET DLL;
源代码如下所示:
#include <ansi_c.h>
#include <cvirte.h>
#include <userint.h>
//有关DLL的信息
static const char * DLL_Name =“ ClassLibrary1,Version = 1.0.0.0,Culture = EN,PublicKeyToken = null”;
int main()
{
int status;
int result;
//声明DLL的句柄
ClassLibrary1_Class1 Handle;
//注册DLL
Status = CDotNetRegisterAssemblyPath(DLL_Name,
“ c:\\ Users \\ kqian \\ Desktop \\ CVI \\ Projects \\ C#net DLL call\\ simple net dll \\ ClassLibrary1.dll”);
//初始化并加载DLL
status = Initialize_ClassLibrary1();
//创建一个实例
status = ClassLibrary1_Class1_Create(&Handle,NULL);
//调用函数
status = ClassLibrary1_Class1_add(Handle,1,3,&result,NULL);

printf(“ Result =%d \ n”,result);
//释放手柄
CDotNetDiscardHandle(Handle);
//释放内存
CDotNetFreeMemory(&result);
//卸载DLL
status = Close_ClassLibrary1();

return 0;
}

用C#和其他.NET语言编写的DLL需要.NET Runtime引擎的支持。 CVI仅通过“创建.NET控制器”方法调用此类DLL。同时,您需要注意数据类型转换的问题。 .NET语言中的Enum,矩形数组,String,System.Decimal和System.Boolean可以自动完成转换,而COM运行时可调用包装(RCW)类型,锯齿状数组和Boxed Data类型则需要手动调用库函数转换。