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

更新 May 25, 2021

環境

軟體

  • LabVIEW

其他

  • Microsoft Visual Studio

如果程式員從未使用過Microsoft Visual C ++(MSVC)整合開發環境(IDE),則可能會感到不知所措。本文檔旨在幫助希望編譯DLL以便與LabVIEW配合使用的人。

請注意:本文檔是使用MSVC 2010編寫的

創建一個DLL專案

請按照Microsoft教程中的步驟創建一個C ++ DLL:Walkthrough: Create and use your own Dynamic Link Library (C++) 
  1. 在Visual Studio中,選擇File»New Project以打開“New Project”對話框。從“ Visual C++ Templates list”列表中,選擇“Win32 Project” ,命名您的專案,然後單擊“OK”
  1. 在下一個對話框中,您可能會看到當前專案設置為Windows應用程式。單擊“Next”將“Application Type”更改為DLL
 
  1. 選擇 Finish以創建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)。為此,請在cpp文件的頂部#include“ stdafx.h”下插入兩行程式碼。 該程式碼應如下所示:

#include "stdafx.h"
#include "math.h"    //library that defines the pow function
#define M_PI 3.14159 //declare our M_PI constant


此時,您可以編譯並鏈接DLL。但是,如果這樣做,則DLL將不會導出任何功能,因此,它實際上不會有用。

導出符號

要存取DLL中的功能,必須告訴編譯器導出所需的符號。但是,您首先必須解決C ++名稱修飾的問題。如果擴展名為.cpp.cxx,則MSVC會將您的源代碼編譯為C ++。如果源文件的擴展名為.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文件。要更改此選項,請選擇Project»Properties。在 Linker 資料夾中,單擊“ Input”屬性頁,然後將“Module Definition File”屬性修改為 /DEF: <filename>.def 

另請參閱微軟的 Exporting from a DLL Using DEF Files


指定呼叫約定

在編譯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(doublt radius, 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