使用Microsoft Visual C ++构建DLL以在LabVIEW中使用

更新 Apr 15, 2021

环境

软件

  • LabVIEW

其他

  • Microsoft Visual Studio

如果程序员从未使用过Microsoft Visual C ++(MSVC)集成开发环境(IDE),则可能会感到不知所措。本文档旨在帮助希望编译DLL以便与LabVIEW配合使用的人。

注意:本文档是使用MSVC 2010编写的

创建一个DLL项目

请按照Microsoft教程中的步骤创建一个C ++ DLL: 演练:创建并使用自己的动态链接库(C ++)
  1. 在Visual Studio中,选择“文件” »“新建项目”以打开“新建项目”对话框。从“ Visual C ++模板”列表中,选择“ Win32项目” ,命名您的项目,然后单击“确定”
  1. 在下一个对话框中,您可能会看到当前项目设置为Windows应用程序。单击“下一步”将“应用程序类型更改为DLL


  1. 选择完成以创建DLL。 MSVC使用一个源( .cpp )文件创建一个与项目同名的DLL项目。它还会生成一个stdafx.cpp文件。 stdafx.cpp文件是必需的,但是通常不需要编辑它。

编辑源文件

  1. 每个DLL文件必须有一个DllMain函数,该函数是该库的入口点。确定您的DllMain函数需要并在必要时编辑该函数。
  • 除非必须对库进行特定的初始化,否则MSVC创建的默认DllMain就足够了。请注意,此功能不执行任何操作。
BOOL APIENTRY DllMain( HANDLE hModule,
                        DWORD  ul_reason_for_call,
                        LPVOID lpReserved )
{
    return TRUE;
}
  • 如果需要初始化库,则可能需要更完整的DllMain
BOOL WINAPI DllMain(  
         HINSTANCEhinstDLL,  // handle to DLL module
         DWORD fdwReason,     // reason for calling function
         LPVOID lpReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason )
    {
    case DLL_PROCESS_ATTACH:
        // Initialize once for each new process.
        // Return FALSE to fail DLL load.            
        break;

    case DLL_THREAD_ATTACH:        
        // Do thread-specific initialization.
        break;        
   
    case DLL_THREAD_DETACH:
        // Do thread-specific cleanup.            
        break;
   
    case DLL_PROCESS_DETACH:        
        // Perform any necessary cleanup.
        break;    
    }
        return TRUE;
}
  1. DllMain函数完成后,编写要从DLL访问的例程。例如:
//Function declarations
int GetSphereSAandVol(double radius, double* sa, double* vol);
double GetSA(double radius);
double GetVol(double radius);

...

int GetSphereSAandVol(double radius, double* sa, double* vol)
//Calculate the surface area and volume of a sphere with given radius
{
    if(radius < 0)
    return false; //return false (0) if radius is negative
        *sa = GetSA(radius);
        *vol = GetVol(radius);
        return true;
}

double GetSA(double radius)
{
    return 4 * M_PI * radius * radius;
}

double GetVol(double radius)
{
    return 4.0/3.0 * M_PI * pow(radius, 3.0);
}
  1. 为了使DLL正确编译,必须声明pow函数(即power, pow(x,y)等于x ^ y)和常数M_PI(即3.14159)。为此,请在的顶部#include“ stdafx.h”下插入两行代码。 cpp文件。该代码应如下所示:


#include“ stdafx.h”
#include“ math.h” //定义pow函数的库
#define M_PI 3.14159 //声明我们的M_PI常量


此时,您可以编译并链接DLL。但是,如果这样做,则DLL将不会导出任何功能,因此,它实际上不会有用。

导出符号

要访问DLL中的功能,必须告诉编译器导出所需的符号。但是,您首先必须解决C ++名称修饰的问题。如果MSVC带有,则将其编译为C ++。 cpp或。 cxx扩展名。如果源文件有一个。 c扩展名,然后MSVC将其编译为C。如果将文件编译为C ++,则函数名称通常在输出代码中修饰。这可能是有问题的,因为函数名称已添加了额外的字符。为避免此问题,请在函数声明中将函数声明为“ extern“ C””,如下所示:

extern“ C” int GetSphereSAandVol(double radius,double * sa,double * vol);

这样可以防止编译器用C ++装饰修饰名称。
警告:如果没有C ++装饰,则不能使用多态函数。

完成C ++装饰后,实际上可以导出函数。有两种方法可以通知链接器导出哪些功能。第一种,也是最简单的方法,是对要导出的任何函数使用函数原型中的__declspec(dllexport)标记。为此,将标签添加到声明和定义中,如下所示:

extern“ C” __declspec(dllexport)int GetSphereSAandVol(double radius,double * sa,double * vol);
...
__declspec(dllexport)int GetSphereSAandVol(double radius
,double * sa,double * vol)
{
...
}


第二种方法是使用。 def文件以明确声明要导出的功能。的。 def文件是一个文本文件,其中包含链接程序用来确定导出内容的信息。它具有以下格式:

LIBRARY   <Name to use inside DLL>
DESCRIPTION "<Description>"
EXPORTS
    <First export>   @1
    <Second export>  @2
    <Third export>   @3
    ...

对于示例DLL,。 def文件将如下所示:
LIBRARY   EasyDLL
DESCRIPTION "Does some sphere stuff."
EXPORTS  
    GetSphereSAandVol   @1


如果您已经正确创建了DLL项目,则链接程序将自动查找。与项目目录中的项目同名的def文件。要更改此选项,请选择项目»属性。在链接器文件夹中,单击“输入”属性页,然后将“模块定义文件”属性修改为/ DEF:<filename> .def

另请参阅Microsoft 使用DEF文件从DLL导出


指定调用约定

在编译DLL之前,您可能需要做的最后一件事是为要导出的函数指定调用约定。通常,有两种选择:C调用约定或标准调用约定,也称为Pascal和WINAPI。大多数DLL函数使用标准的调用约定,但LabVIEW可以调用任何一种。

要指定C调用约定,您无需执行任何操作。除非您在Project»Properties»C / C ++»Advanced中另外指定,否则这是默认设置。如果要将函数显式声明为C调用,请在函数声明和定义中使用__cdecl关键字:

extern“ C” __declspec(dllexport)int __cdecl GetSphereSAandVol(double radius,double * sa,double * vol);
...
__declspec(dllexport)int __cdecl GetSphereSAandVol(半径加倍,double * sa,double * vol)
{
...
}


要指定标准调用约定,请将__stdcall关键字放在函数声明和定义中:
extern "C" int __stdcall GetSphereSAandVol(double radius, double* sa, double* vol);
...
int __stdcall GetSphereSAandVol(doublt radius, double* sa, double* vol)
{
     ...
}

使用标准的调用约定时,函数名称在DLL中修饰。您可以使用来避免这种情况。导出功能的def文件方法,而不是__declspec(dllexport)方法。因此,NI建议您使用def文件方法来导出stdcall函数。


 

建立DLL

编写代码,声明要导出的函数并设置调用约定后,就可以构建DLL了。选择Build » Build <您的项目>来编译并链接您的DLL。现在,您可以使用LabVIEW调试DLL了。附带的EasyDLL.zip文件包含用于创建此DLL的Visual C ++工作区以及用于访问该DLL的LabVIEW VI。

下一步

Attachments