1. 程式人生 > >關鍵技術4:從CVI中匯出dll供外部使用

關鍵技術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);
 ...
為了能夠使面板能夠使用dll中callback函式,必須使用LoadPanelEx載入之:  if ((panelHandle = LoadPanelEx (0, "testCVI.uir", PANEL, __CVIUserHInst ) ) < 0)
  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

4、嵌入UIR面板。舊版的LabWindows/CVI不支援嵌入面板,因此在使用CVI生成的dll同時,也需要將uir面板拷貝到相同的執行時路徑(./Debug/)下。2009版的LabWindows/CVI則可以直接配置為嵌入UIR檔案:
通過在該配置頁面設定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的官方例子 為基礎,修正若干錯誤。下載分別在 、和