2015年8月12日 星期三

[原創] LabVIEW wrapper Callback from dll (3) Example:EnumWindows

最近有網友來信詢問於在LabVIEW中如何使用”User32.dll”中API EnumWindows由於它是使用callback的方式傳回資料因此需封裝成dll的形式給LabVIEW使用,本篇將介紹此過程的程式撰寫方式。
LabVIEW中若想要使用dll來註冊Eventdll必須加入extcode.h這個檔頭和使用Librarylabview.liblabviewv.lib
對於到底是使用labview.liblabviewv.lib,根據這篇文章(labview.lib or labviewv.lib?[http://forums.ni.com/t5/LabVIEW/labview-lib-or-labviewv-lib/td-p/2916894]),我的理解是為了對應不同版本的LabVIEW RunTime,應該要使用labviewv.lib
首先由微軟的MSDN查看EnumWindows的使用說明:
BOOL WINAPI EnumWindows(
  _In_ WNDENUMPROC lpEnumFunc,
  _In_ LPARAM      lParam
);

Parameters

lpEnumFunc [in]
Type: WNDENUMPROC
A pointer to an application-defined callback function. For more information, seeEnumWindowsProc.
lParam [in]
Type: LPARAM
An application-defined value to be passed to the callback function.

Return value

Type:
Type: BOOL
If the function succeeds, the return value is nonzero.
由此可看出EnumWindows需要輸入的參數,lpEnumFunc 就是callback函數的原型,要傳入實作函數程式碼的位址,lParam 則是輸入的參數,要傳入未定的資料型態位址。
接著再看看EnumWindowsProc的函數原型定義:
BOOL CALLBACK EnumWindowsProc(
  _In_ HWND   hwnd,
  _In_ LPARAM lParam
);

Parameters

hwnd [in]
A handle to a top-level window.
lParam [in]
The application-defined value given in EnumWindows or EnumDesktopWindows.

Return value

To continue enumeration, the callback function must return TRUE; to stop enumeration, it must return FALSE.
由此可知EnumWindowsProc這個callback函數的原型宣告方式,當callback發生時系統會提供hwnd就是我們想要列舉的程式handle值,而lParam在這邊並沒有用到。
有了這些資訊就可以開始編寫提供給LabVIEWdll,首先先定義回傳給LabVIEW的資料型態,由於希望取得的是目標程式的類別字串和視窗名稱自串,因此宣告一個結構包和兩個字串:
typedef struct tagStrRec {
          LStrHandle Str1;
          LStrHandle Str2;
} StrRec, *StrRecPtr;
這樣的話在LabVIEW的部分就可以解成cluster包含兩個String的結構
接著宣告全局變數用於儲存LabVIEW UserEvent的位址和常數。
// 紀錄LabVIEW UserEvent的位址
LVUserEventRef EnumWindowsEvent;
// 預設最長字串長度為256 Bytes
#define STRING_LENGHT 256
然後再宣告dll輸出的函數
//用於註冊LabVIEW事件
MgErr __declspec(dllexport) __cdecl RegisterEnumWindowsEvent(LVUserEventRef *value);
//用於呼叫dllEnumerateWindows
void __declspec(dllexport) __cdecl EnumerateWindows(void);
實作這兩個函數與callback函數就完成dll的部分
MgErr __declspec(dllexport) __cdecl RegisterEnumWindowsEvent(LVUserEventRef *value)
{
          //用於紀錄LabVIEW程式內Event的位址
          EnumWindowsEvent = *value;
                    return 1;
}
void __declspec(dllexport) __cdecl EnumerateWindows(void)
{
          //透過dll呼叫EnumWindows,並傳入dll內宣告的callback函數位址。
          EnumWindows(EnumWindowsProc, NULL);
}

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
          char class_name[STRING_LENGHT];
          char title[STRING_LENGHT];
          StrRec returnData;
          // init labview string
          returnData.Str1=(LStrHandle)DSNewHandle(sizeof(int32)+STRING_LENGHT*sizeof(uChar));
          returnData.Str2=(LStrHandle)DSNewHandle(sizeof(int32)+STRING_LENGHT*sizeof(uChar));
          // get windows class and title data
          GetClassName(hwnd,class_name, sizeof(class_name));
          GetWindowText(hwnd,title,sizeof(title));
          // convert char arr to labview string
          PopulateStringHandle(returnData.Str1,class_name);
          PopulateStringHandle(returnData.Str2,title);
          // use labview event to send back data
          PostLVUserEvent(EnumWindowsEvent,(void *)&returnData);

          return TRUE;
}

LabVIEW程式的部分主要就是dll的呼叫與User Event的註冊,這邊就直接看圖說故事了




執行後的畫面如下,可以列舉到記事本的類字串與視窗字串: