轉自:http://bbs.elecfans.com/jishu_469502_1_1.html
LabVIEW調用DLL文件
LabVIEW支持經過調用DLL文件的方式與其它編程語言混合使用。好比,在實際的工程項目中,用戶能夠用C++語言實現軟件的運算部分,並把這些功能構建在DLL文件中,而後再使用 LabVIEW編寫程序的界面部分,並經過調用編寫好的DLL來調用運算部分的功能。
LabVIEW 中是經過Call Library Function Node(CLN)節點來完成DLL文件調用的。建立一個新的VI,右擊程序框圖,在Functions Palette中依次選中Connectivity——Libraries&Executables工具欄便可找到該節點(圖1)。
<ignore_js_op>
圖1 Call Library Function Node
將節點放置在程序框圖中,雙擊會出現它的配置對話框,共有四頁。第一頁用於填寫被調用函數的信息(圖2)。Library name or path需給出DLL文件名和路徑,操做系統路徑下的DLL文件,直接輸入文件名也可調用,不然必須輸入全路徑。在這裏已經給出名字的DLL是被靜態加載到程序中的,也就是說當調用了這個DLL的VI被裝入內存時,DLL同時被裝入內存。LabVIEW也可動態加載DLL,只要勾選上Specify path on diagram的選項便可。選擇了這個選項,在 Library name or path中輸入的內容就無效了,取而代之的是CLN 節點多出一對輸入輸出,用於指明所須要使用的DLL的路徑。這樣,當VI被打開時,DLL不會被裝入內存,只用程序運行到須要使用這個DLL中的函數時,才把其裝入內存。Function name是須要調用的函數的名稱,LabVIEW會把DLL中全部的暴露出來的函數都列出,用戶只要在下拉框中選取便可。Thread欄用於設定哪一個線程裏運行被調用的函數。用戶能夠經過 CLN 節點的配置面板來指定被調用函數運行所在的線程。CLN 的線程選項很是簡單,只有兩項: Run in UI thread和Run in any thread。LabVIEW的程序框圖上直接能夠看出一個 CLN節點是選用
的什麼線程。若是Run in UI thread,節點顏色是橙色的;Run in any thread則是淺黃色的(圖3)。
<ignore_js_op>
圖2 填寫被調用函數信息
<ignore_js_op>
圖3 CLN不一樣線程對比
一般狀況下,除非使用的動態連接庫是多線程安全的,CLN 中選擇Run in any thread方式;不然必須選擇Run in UI thread方式。判斷一個動態連接庫是否是多線程安全的,需經過如下方法:若是一個動態連接庫的文檔中沒有明確說明它是多線程安全的,那麼就要看成是非多線程安全的;在能夠看到動態連接庫源代碼的條件下,若是代碼中存在全局變量、靜態變量或者代碼中看不到有lock一類的操做,那麼這個動態連接庫也就確定不是多線程安全的。
選擇了Run in any thread方式,LabVIEW會在最方便的線程內運行動態連接庫函數,且通常會與調用它的VI在同一個線程內運行。由於LabVIEW是自動多線程的語言,它也極可能會把動態連接庫函數分配給一個單獨的線程運行。若是程序中存在沒有直接或間接前後關係的兩個CLN節點,LabVIEW極可能會同時在不一樣的線程內運行它們所調用的函數,也許是同一函數。對於非多線程安全的動態連接庫,這是很危險的操做。很容易引發數據混亂,甚至是程序崩潰。
選擇Run in UI thread方式,由於LabVIEW只有一個界面線程,因此若是全部的CLN設置都是界面線程,那麼就能夠保證這些CLN調用的函數確定所有都運行在同一線程下,確定不會被同時調用。對於非多線程安全的動態連接庫,這種方式就保證了它的安全。
讓咱們回到配置對話框第一頁,Calling convention用於指明被調用函數的調用約定。這裏只支持兩種約定:stdcall和C call。它們之間的區別在於,stdcall由被調用者負責清理堆棧,C call由調用者清理堆棧。這個設置錯誤時,可能會引發LabVIEW崩潰,也就是說若是LabVIEW調用DLL函數時出現異常,首先應該考慮這個設置是否正確。(Windows API通常使用的都是stdcall;標準C的庫函數大多使用C call。若是函數聲明中有相似__stdcall這樣的關鍵字,它就是stdcall的。)第二頁是函數參數的配置(圖4)。
<ignore_js_>
圖4 配置函數的參數
DLL和LabVIEW之間傳遞參數,最經常使用的三種數據類型是數值、數值型數組和字符串。C語言中常常把指針或者數據的地址在函數間傳遞,在32位操做系統中,可使用int32數值來表示指針。所以,當須要在LabVIEW中傳遞指針數據時,可使用I32或U32數值類型來表示這個地址類型的數據。可是,64位的程序中,數據的地址只能使用I64或U64來表示。這樣,若是一個調用了DLL函數的VI,而且函數參數中有地址型數據,使用固定數據類型的數值來表示地址,就要準備兩份代碼。解決方法是使用LabVIEW中的新的數據類型Pointer-sized Integer。這個數據類型的長度在不一樣的平臺上會自動使用32位或64位長度。若是在C語言函數參數聲明中有const關鍵字,能夠選中Constant選項。布爾類型在DLL函數和LabVIEW VI之間傳遞沒有專有的數據類型,是利用數值類型來傳遞的。輸入時先把布爾值轉變爲數值,在傳遞給DLL函數;輸出時再把數值轉爲布爾值。對於數組的傳遞,LabVIEW只支持C數據類型中的數值型數組,傳遞數組類型須要注意的的是「Array Format」要選擇「Array Data Pointer」。這個設置中還有其餘兩個選項,帶有「Handle」的參數類型都是表示LabVIEW定義的特殊類型的。在第三方的DLL中不會使用到數組參數做爲輸出值時,要記得爲輸出的數組數開闢空間。開闢數據空間的方法有兩種:第一種方法,建立一個長度知足要求的數組,做爲初始值傳遞給參數,輸出數的數據就會被放置在輸入數組的所在的內存空間內。第二種方法是直接在參數配置面板上進行設置。在Minimum size中寫入一個固定的數值,LabVIEW就會按此大小爲輸出的數組開闢空間。在 Minimum size 中選擇函數的其它數值參數,而不是固定數值。這樣LabVIEW會按照當時被選擇的參數值的大小來開闢空間。字符串與使用與數組是很是相似的,實際上在C語言中字符串就是一個I8數組。
在NI軟件的安裝路徑下打開當前使用版本的LabVIEW文件夾,經過 examples\dll\data passing\Call NativeCode.llb找到簡單數據類型在LabVIEW與C之間的對應關係。部分常見關係見表1。
<ignore_js_op>
<ignore_js_op>
<ignore_js_op>
<ignore_js_op>
表1 數據類型對比
第三頁用於爲DLL設置一些回調函數,可使用這些回調函數在特定的情形下完成初始化、清理資源等工做(圖5)。
<ignore_js_op>
圖5 設置回調函數
若是爲Reserve選擇了一個回調函數,那麼當一個新的線程開始調用這個DLL時,這個回調函數首先被調用。能夠利用這個函數爲新線程使用到的數據作初始化工做。線程在使用完這個DLL以後,它會去調用Unreserve中指定的回調函數。Abort中指定的函數用於VI非正常結束時被調用,也就是讓一個程序在運行完前中止。這些回調函數的原型在Prototype for these procedures中列出,必需要由DLL的開發者按照特定的格式實現。若是使用的DLL不是專爲LabVIEW設計的,通常不會包含這樣的回調函數。
第四頁是錯誤處理方式,用戶可根據須要選擇相應的錯誤檢查級別。
另外還須要注意的是,C語言中的struct在LabVIEW中可使用cluster來表示,但有時須要做出相應的調整。這是由於在C語言中,struct的字節對齊是能夠進行設置的,這就決定了其各元素的存放地址的可變性。C語言中的對字節對齊數可經過#pragma pack指令或在工程屬性中進行指定。而在LabVIEW的cluster中,全部元素只能是1字節對齊的,因此若是要和C語言中非1字節對齊的struct對應,須要作出一些調整。好比,對於C語言中2字節對齊的struct,第一個元素若是是I8型的,在LabVIEW的cluster中第一個元素對應不變,但不能緊挨着放第二個元素,必須留一個無心義的空位。C語言的struct其實也是如此,只不過沒有表現出來。因此爲了方便,若是本身用C語言生成DLL文件供LabVIEW調用最好將struct都設爲1字節對齊。C語言的struct中能夠嵌套數組,可是這和LabVIEW中含有數組元素的cluster是不同的,LabVIEW中須要將數組中的元素都拆開放入cluster中。
若是C語言的struct中含有一個指針,LabVIEW中的cluster只能用一個U32數值(32位系統上,64位系統上使用U64)來表示指針的地址,而不能將指針所指向的內容放到Cluster中去。若是聲明的是指向struct的指針,才能在LabVIEW中使用cluster與之對應。CLN節點的配置面板中,沒有一個專門命名的「struct」或者「cluster」參數類型,應選擇「Adapt to Type」就能夠了。若是參數的類型就是結構而非指針,考慮到C函數參數的壓棧順序,把一個結構體做爲參數傳給函數,至關於把結構中每一個元素分別做爲參數傳遞給函數。圖6爲C語言中struct和LabVIEW中cluster的部分匹配圖。
<ignore_js_op>
圖6 struct和cluster匹配
LabVIEW打包DLL文件
咱們接下來學習如何使用LabVIEW來打包一個DLL文件。
首先咱們編寫一個名爲Scale.vi的程序,功能很簡單就是對輸入的數據乘上10,而後再輸出(圖7)。
<ignore_js_op>
圖7 scale.vi
必須在任務管理器中才能生成.dll文件。因此咱們首先創建一個project,過程以下:
點擊File>>New Project:
<ignore_js_op>
圖8 生成新項目
接着彈出是否將該VI添加到新項目的對話框:
<ignore_js_op>
圖9 是否添加VI到新建項目
選擇Add,生成新的項目管理器,將其保存在須要的路徑下:
<ignore_js_op>
圖10 項目管理器
右鍵單擊項目瀏覽器窗口中的Build Specifications,在快捷菜單中選擇New>>Shared Library(DLL),彈出對DLL文件進行設置的對話框。點擊Category>>Information,根據本身需求修改Build specification name和Target filename:
<ignore_js_op>
圖11 Information頁面
點擊Source Files>>Project Files>> Scale.vi>> ,彈出對話框,直接用默認值,點擊OK:
<ignore_js_op>
圖12 Define VI Prototype
點擊Destination>> Scale.dll,點擊 ,可選擇須要保存的路徑。而後再點擊Support Directory,這是指明瞭DLL支持文件的路徑(好比數據文件之類的放在哪一個文件夾),選擇默認便可:
<ignore_js_op>
圖13 Destination頁面
Category中的Source Files可供用戶對打包VI的屬性和密碼作一些設置;Advanced和Additional Exclusions能夠作一些高級的設置,這些均按默認值便可。Version Information可以讓用戶填寫版本號、名稱、版權、公司等信息:
<ignore_js_op>
圖14 Version Information
點擊Run-Time Languages,可對支持語言進行選擇,默認便可。點擊Preview>>Generate Preview,能夠預覽到結果:
<ignore_js_op>
圖15 預覽生成
點擊Build,彈出生成狀態對話框:
<ignore_js_op>
圖16 生成狀態框
點擊Done,生成完成,打開DLL文件保存的路徑查看:
<ignore_js_op>
圖17 DLL文件保存路徑
LabVIEW調用DLL文件
LabVIEW能夠方便地調用DLL文件,這些DLL文件能夠是其餘編譯工具,如VC,生成的。
LabVIEW能夠直接經過CLN節點來調用DLL文件,之前面生成的Scale.dll文件爲例。現有一個內部定時連續採集程序,經過調用該DLL文件,使讀取的值爲實際採集值的10倍(圖18)。
<ignore_js_op>
圖18 連續採集程序
方法一
在程序框圖放入Call Library Function Node,雙擊彈出對話框。在Function頁面的Library name or path中給入生成的Scale.dll文件的路徑,Function name選擇Scale,其餘選項默認。
<ignore_js_op>
圖19 Function頁面
因爲是LabVIEW生成的DLL文件,在Parameters頁面不須要作改動,可是因爲VI還有一路輸出,因此還須要添加一個參數y,做爲DLL文件的輸出。
若是是C語言等非LabVIEW生成的DLL函數,須要將retuen type的type選項和Data type選項改爲函數定義的參數類型,對於函數裏輸入的參數也都須要自行添加。Callbacks和Error Checking則不須要改動。
<ignore_js_op>
圖20 Parameters頁面
點擊「OK」,將生成的CLN的輸入段鏈接到DAQmx Read.vi,return type輸出鏈接到波形圖表上,便可實現採集值放大10倍的功能。
<ignore_js_op>
圖21 完成後的程序
方法二
LabVIEW中還有一種方法能夠調用DLL文件,在VI的選項欄,依次選擇Tools——Import——Shared Library(.dll),彈出Import Shared Library對話框。
<ignore_js_op>
圖22 生成Import Shared Library對話框
選擇Create VIs for a shared library,點擊Next,在Shared Library(.dll) Files中輸入Scale.dll文件的路徑,Head(.h) File裏填寫頭文件的路徑。
<ignore_js_op>
圖23 選擇DLL文件路徑和頭文件路徑
點擊Next,若是DLL文件中依賴其餘的一些DLL文件,須要在Include Paths中填寫這些文件的路徑。其餘選項能夠根據客戶需求設置,通常默認便可。這樣一直點擊Next到最後,選擇Open the generated library,點擊Finish。這樣能夠生成一個.lvlib格式的庫文件,裏面包含了Scale.vi,這是將調用該DLL文件的方法封裝好的VI,只留下輸入和輸出接口,方便運用到LabVIEW的程序中。直接將Scale.vi拖放到剛纔的連續採集中便可完成方法一的功能。
<ignore_js_op>
圖24 完成的程序
VC調用LabVIEW生成的DLL文件
剛纔介紹了LabVIEW調用DLL文件的方法,使用VC調用LabVIEW生成的DLL文件也很簡單。仍是以以前生成Scale 的DLL文件爲例,不一樣的是採集電壓程序使用的是C語言的例程,但和LavVIEW實現的功能相同。
首先將先前生成Scale DLL文件時,路徑下全部的文件所有複製粘帖到C語言例程的文件夾下。打開連續採集程序,點擊狀態欄的Project——Settings,在Project Settings對話框中加載入Scale.lib的靜態連接庫。
在程序中鍵入#include "Scale.h",以便引入該DLL函數。下面是C程序的代碼,功能是有限點採集電壓,經過Scale.dll文件實現採樣值放大10倍的功能。加粗部分是由於調用DLL文件所作的改動。
1 #include
2 #include "NIDAQmx.h"
3 #include "Scale.h"
4
5 #define DAQmxErrChk(functionCall) if( DAQmxFailed(error=(functionCall)) ) goto Error; else
6
7
8
9 int main(void)
10
11 {
12
13 int32 error=0;
14
15 TaskHandle taskHandle=0;
16
17 int32 read;
18
19 float64 data[1000];
20
21 char errBuff[2048]={'\0'};
22
23 int i=0;
24
25 double x10=0;
26
27
28
29
30
31 /*********************************************/
32
33 // DAQmx Configure Code
34
35 /*********************************************/
36
37 DAQmxErrChk (DAQmxCreateTask("",&taskHandle));
38
39 DAQmxErrChk (DAQmxCreateAIVoltageChan(taskHandle,"Dev1/ai0"/*Config correct device*/,"",DAQmx_Val_Cfg_Default,-10.0,10.0,DAQmx_Val_Volts,NULL));//
40
41 DAQmxErrChk (DAQmxCfgSampClkTiming(taskHandle,"",10000.0,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,1000));
42
43
44
45 /*********************************************/
46
47 // DAQmx Start Code
48
49 /*********************************************/
50
51 DAQmxErrChk (DAQmxStartTask(taskHandle));
52
53
54
55 /*********************************************/
56
57 // DAQmx Read Code
58
59 /*********************************************/
60
61 DAQmxErrChk (DAQmxReadAnalogF64(taskHandle,1000,10.0,DAQmx_Val_GroupByChannel,data,1000,&read,NULL));
62
63
64
65 printf("Acquired %d points\n",read);
66
67
68
69 for(i=0;i<1000;i++){
70
71 Scale(data, &x10);
72
73 printf("the %d Value is : %f \n",i,x10);
74
75
76
77 }
78
79 Error:
80
81 if( DAQmxFailed(error) )
82
83 DAQmxGetExtendedErrorInfo(errBuff,2048);
84
85 if( taskHandle!=0 ) {
86
87 /*********************************************/
88
89 // DAQmx Stop Code
90
91 /*********************************************/
92
93 DAQmxStopTask(taskHandle);
94
95 DAQmxClearTask(taskHandle);
96
97 }
98
99 if( DAQmxFailed(error) )
100
101 printf("DAQmx Error: %s\n",errBuff);
102
103 printf("End of program, press Enter key to quit\n");
104
105 getchar();
106
107 return 0;
108
109 }
因此,使用LabVIEW不只能夠方便地調用各類編譯軟件生成的DLL文件,本身也能生成DLL文件供其餘編譯軟件調用。這樣,用戶在編寫大型項目時更加靈活,也爲熟悉C語言的工程師提供了巨大的方便。本文只對LabVIEW和VC相互調用DLL文件作了簡單的介紹,不少高級功能和技巧,用戶能夠在實際運用中逐漸掌握。