關鍵技術4:從CVI中匯出dll供外部使用
NI的LabWindows/CVI 開發環境已經升級到2009版,不僅可以使用Windows下 Borland C++、Visual C++編譯的dll,而且可以直接用這些外部編譯器替代CVI本身的編譯器來編譯原來的程式碼資源!相比原來LabWindows/CVI 5.5 ,2009版在多編譯器環境下協同開發的阻力大大減少,提供了諸多便利,由此組合出多編譯環境的協同開發方法至少由9種。這裡,考慮的是:將現有的CVI工程程式碼轉換為dll,嵌入到Visual C++工程中去。用意很顯然——用於半實物模擬系統構造。 實現這一技術主要分為兩階段: 1、在CVI中重新配置現有CVI工程程式碼,生成Visual C++可用的動態連結庫; 2、在新的Visual C++做必要修改,呼叫CVI的動態連結庫。 詳細步驟如下: 一、CV部分,主要是程式碼上的修改和配置上的修改。 程式碼上,找出原有CVI工程的程式入口檔案,一般是main所在C檔案,修改之或者新建C檔案包含之,相當於製造一個dll外殼。這個外殼至少要包含以下程式碼:
#include <cvirte.h>
int __stdcall DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
if (InitCVIRTE (hinstDLL, 0, 0) == 0)
return 0; /* out of memory */
break;
case DLL_PROCESS_DETACH:
CloseCVIRTE ();
break;
}
return 1;
}
int __stdcall DllEntryPoint (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
/* Included for compatibility with Borland */
return DllMain (hinstDLL, fdwReason, lpvReserved);
}
這段程式碼基本上是作業系統的要求,不涉及CVI執行時系統。要生成可供外部實用的dll,程式碼方面還有一個細微的修改:修改面板的載入方式。一般的main函式載入面板的方式如下: if ((panelHandle = LoadPanel (0, "testCVI.uir", PANEL) ) < 0)
return;
DisplayPanel (panelHandle);
RunUserInterface ();
DiscardPanel (panelHandle);
...
return;
DisplayPanel (panelHandle);
RunUserInterface ();
DiscardPanel (panelHandle);
... 其中的__CVIUserHInst 是預設的dll載入控制代碼。 不僅是main函式,整個工程程式碼中的LoadPanel都必須如上修改。 CVI編譯配置還需要做一些修改。 1、Build -> Target Type 設定為Dynamic Link Library 2、Option-> Build Option,檢查其中的函式呼叫方式,確保與Visual C++ 保持一致。一般WINAPI使用的是__stdcall方式,這裡選擇__stdcall。 3、Build -> Target Settings 設定匯出函式的方式為使用專門為dll編寫的"Include file"的符號: 不使用與UIR面板同名的testCVI.h是為了避免修改testCVI.UIR後自動覆蓋了它。採用單獨的.h來指定dll的匯出函式更加安全。testCVIDLL.h中是關於dll的匯出資訊,用於外部編譯器在編譯期使用。由於Visual C++預設是C++編譯模式,而CVI向來是標準C編譯模式,因此,這個標頭檔案必須使用C巨集保護:
/*if using C++ Compiler to compile the file, adopting C linkage mode
*/
#ifdef __cplusplus
extern "C" {
#endif
/* your function declaration for dll */
#ifdef __cplusplus
}
#endif
通過在該配置頁面設定Custom Copy directory,甚至連拷貝dll到Visual C++工程下的執行時目錄這一步也省了。 5、Ctrl+M 生成 dll、lib的debug版或者release版。 二、Visual C++部分 主要是編譯設定和連結設定。 1、設定函式呼叫方式與CVI保持一致: 2、將dll的編譯頭lib新增進來: 需要說明的是,新增lib檔案有多種方式:(1)直接add file;(2)新增目錄到庫搜尋路徑,然後給出庫的檔名;(3)直接新增庫的路徑名。標頭檔案的新增也是類似的,這裡是將dll的標頭檔案直接複製到了工程根目錄下,因此無須多勞。另外,在Visual C++的編譯器選項中,/Gz指定__stdcall呼叫方式, /Gd 指定__cdecl呼叫方式。
這個例子最後是實現在MFC程式中呼叫CVI面板程式:
常見問題:
1、連結錯誤2001:找不到dll中符號。這是由於Visual C++預設對*.cpp檔案中的函式呼叫(包括dl中的匯出函式)採用C++命名方式造成的,需要使用 extern "C"巨集進行保護。 思路來自這裡 關於dll呼叫原理的解釋和dll標頭檔案格式的分析 。
相關資源: 1、本文例項 以NI的官方例子 為基礎,修正若干錯誤。下載分別在這 、和這 。