編寫資源DLL
開始
可以使用MS Visual C++開發系統或其它C/C++開發工具編寫自定義資源DLL本文中例子使用的是
Microsoft Visual C++ version b 包含Unicode MFC庫
MIDL編譯器版本(該MIDL編譯器可在SDK中找到)
活動模板庫(ATL)版本(擴展例子代碼和資源類型生成向導產生的代碼需要它)
建立編譯環境時請參考平台SDK特別是Preparing a Build Environment和Developer Notes章節
在平台SDK的例子中也可找到完整的資源DLL參考實現(參見SMBsmp)
創建新資源類型
要創建新的資源類型必須寫一個資源DLL和一個群集管理器擴展DLL簡單的方法是運行資源類型生成向導來創建資源DLL該向導將箭竹一個資源DLL框架和/或包含入口點定義申明導出的群集管理器擴展DLL
創建資源DLL的完整步驟請參考SDK文檔的Creating a Custom Resource TypeUsing the Resource Type AppWizard和Customizing a Resource DLL等章節
生成的資源DLL框架僅包含最基本的故障轉移和故障恢復功能要使用群集環境的全部功能和允許DLL提供資源的特定信息需要編寫自己的代碼注意由向導生成的框架代碼中包含TODO和ADDPARAM:注釋以指明哪裡需要添加資源的特定信息像下面所描述的那樣你需要使用資源API完成大部分的自定義功能
自定義資源DLL
如前所述資源API包含幾個入口點函數這些函數在資源DLL內實現資源監視器使用這些入口點函數管理DLL提供的資源另外資源監視器實現少數幾個回調函數資源DLL使用這些回調函數向群集服務報告狀態或為系統管理員記錄事件日志
大部分入口點函數是所有資源必需的兩個特別的API入口點函數-Arbitrate和Release-僅在編寫仲裁資源時需要本文不討論這兩個函數其余的入口點函數在下面列出本文將討論它們的細節
Startup
Open
Online
LooksAlive
IsAlive
Offline
Close
Terminate
ResourceControl
ResourceTypeControl
每個由群集軟件支持的資源DLL應該遵從下面的指引
在某個例外情況下對於給定的資源實例資源DLL是不可重入的該情況是Terminate入口點函數Terminate應該在任何時候都能被調用即使資源DLL中線程處於等待Online或Offline調用完成的阻塞狀態
資源DLL對於其它資源ID是可重入的如果資源DLL擁有超過個資源ID就必須為DLL內所有共享的全局數據進行同步
在資源DLL內一個入口點函數完成操作所花費的時間不應該超過毫秒如果一個入口點函數-特別是Online Offline LooksAlive或isAlive-超出了這個限制就應該派生線程來處理耗時的操作(注意當前向導會為Online生成線程未來版本也應該為Offline生成一個線程)
在資源DLL初始化期間DLL的入口點函數(系統加載DLL後的標准的DLL入口)以DLL_PROCESS_ATTACH標志被調用接著資源監視器開始調用資源API入口點函數
Startup例程
當資源DLL被加載後資源監視器就調用Startup例程注意僅有Startup入口點函數被導出所有在資源DLL內實現的其它入口點函數通過Startup返回的函數表來訪問
下面是Startup例程的定義
DWORD WINAPI Startup
(
LPCWSTR ResourceType
DWORD MinVersionSupported
DWORD MaxVersionSupported
PSET_RESOURCE_STATUS_ROUTINE SetResourceStatus
PLOG_EVENT_ROUTINE LogEvent
PCLRES_FUNCTION_TABLE * FunctionTable
);
ResourceType參數標識被啟動的資源的類型
SetResourceStatus和LogEvent是由資源監視器實現的回調函數(本文後面章節將討論這些回調函數)在Online或Offline入口點函數被調用後如果它們要花費超過毫秒資源DLL應該調用SetResourceStatus向群集告知資源狀態資源DLL也應該使用LogEvent報告事件和錯誤(SetResourceStatus應該僅由Online或Offline調用並且僅在Online或Offline返回ERROR_IO_PENDING的情況更多的信息請參考Online和Offline的討論)
FunctionTable結構包含了資源DLL其余入口點的函數地址
注意 Startup入口點函數資源DLL保存回調函數LogEvent和SetResourceStatus的僅有的地方
Startup返回以下值
如果請求成功返回ERROR_SUCCESS
如果資源不支持在MinVersionSupported和MaxVersionSupported之間的版本返回ERROR_REVISION_MISMATCH
如果操作不成功返回Win?編程接口的錯誤值
出於對資源操作的優化要確保實現的Startup能夠在毫秒內完成
Open例程
一旦Startup成功返回典型地資源監視器會為由資源DLL管理的每個資源調用Open入口點函數
Open例程的下定義如下
RESID WINAPI Open
(
LPCWSTR ResourceName
HKEY ResourceKey
RESOURCE_HANDLE ResourceHandle
);
ResourceName參數標識指定了被打開的資源(一個資源DLL能夠提供給定類型的多個資源)ResourceKey參數是關於資源的特定信息是私有屬性並且位於群集數據庫中該鍵值在Open返回時關閉因此如果該資源在其它入口點函數中訪問一個鍵值DLL應該調用ClusterRegOpenKey或ClusterRegCreateKeyResourceHandle參數在SetResourceStatus和LogEvent回調函數中使用
Open使用群集API打開群集數據庫並取得資源參數和私有屬性一個重要的事情是資源DLL應該在Open入口檢查資源當前是否脫機(一個資源不能同時在超過一個節點上處於聯機狀態)如果資源當前正聯機DLL應該嘗試將其脫機(注意 在這種情況下online和offline指明了應用程序或服務的狀態而不是給定的群集節點-資源必須是真正脫機並且沒有被節點所擁有)另外在Open例程中資源DLL應該為資源創建專屬的數據結構
注意 如果資源為仲裁資源則群集API在Open例程中是不可用的
Open返回以下值
如果操作成功返回資源標識(RESID)
如果操作不成功返回NULL應該調用SetLastError指明發生的錯誤
如果Open返回錯誤(返回NULL)資源將是不可管理的因此Open應該僅在相當稀有的情況返回錯誤(例如不能為資源分配內存)
出於對資源的優化操作確保實現的Open將在毫秒內完成
Online例程
當資源被打開資源監視器調用Online入口點函數將資源聯機
Online例程定義如下
DWORD WINAPI Online
(
RESID ResourceId
PHANDLE EventHandle
);
ResourceId參數被傳遞給該入口函數是資源的唯一標識(與Open入口函數返回的值是同一個)資源DLL能夠將EventHandle參數傳回給資源監視器以便異步通知資源監視器自身狀態如果EventHandle參數不是能夠被信號激發(singal) 的有效句柄那麼資源監視器將周期性的調用資源DLL的LooksAlive入口函數以檢查資源狀態如果不想資源被這種方法煩擾DLL應該在EventHandle參數中返回有效的句柄通過返回有效的EventHandle資源DLL可以向資源監視器通知任何狀態改變
每個資源類型必須有自己的Online入口函數實現這種不同實現是必須的因為不同類型的資源有不同的需要例如將磁盤聯機與將普通應用程序聯機是完全不同的將磁盤聯機涉及到裝載磁盤校驗磁盤簽名等等而將應用程序聯機只需簡單的調用CreateProcess
Online返回以下值
如果操作成功並且資源現在聯機返回ERROR_SUCCESS
如果資源被某些其它系統獨占並且這些其它系統之一擁有獨占權返回ERROR_RESOURCE_NOT_AVAILABLE
如果請求正處於等待狀態並且一個線程已經被激活以處理該請求則返回ERROR_IO_PENDING
如果操作不成功Online返回Win錯誤值
出於對資源操作的優化確保Online例程的實現能夠在毫秒完成處理如果做不到應該在派生一個將資源聯機的工作線程後立即返回ERROR_IO_PENDING給資源監視器SetResourceStatus回調函數(該函數地址保存在Startup入口函數中)應該被周期性的調用以指示資源狀態一旦資源聯機工作線程應該被跟中止或掛起以留待未來使用
如果出於任何原因資源在聯機時失敗了資源DLL應該使用LogEvent回調函數記錄事件日志並且應該調用SetResourceStatus函數SetResourceStatus使用RESOURCE_STATUS結構來指示資源的聯機或脫機狀態群集資源可以是下述狀態之一
聯機—狀態代碼ClusterResourceOnline
脫機—狀態代碼ClusterResourceOffline
失敗—狀態代碼ClusterResourceFailed
等待聯機—狀態代碼ClusterResourceOnlinePending
等待脫機—狀態代碼ClusterResourceOfflinePending
SDK關於SetResourceStatus的更多信息請參考平台SDK
如果在分鐘後資源仍沒有聯機資源監視器將調用Terminate入口函數放棄操作如果一個資源需要花費超過分鐘才能聯機使用群集API的群集管理器Clusterexe或者其它管理工具可以使用ClusterResourceControl通過控制代碼CLUSCTL_RESOUCE_SET_COMM_PROPERTIES修改PendingTimeout公用屬性(更多關於群集控制代碼公用屬性私有屬性等的信息請參考平台SDK文檔
LooksAlive和IsAlive例程
一旦資源聯機資源監視器將周期性檢查資源狀態資源監視器使用LooksAlive和IsAlive入口函數來完成這個操作資源監視器使用LooksAlive進行臨時檢查IsAlive則做通透的檢查
LooksAlive例程定義如下
BOOL WINAPI LooksAlive
(
RESID ResourceId
);
IsAlive例程定義如下
BOOL WINAPI IsAlive
(
RESID ResourceId
);
在這兩個例程中ResourceId參數唯一標識了被檢查的資源實例典型情況下LooksAlive用於進行簡單檢查(例如檢測進程是否仍在運行文件共享是否仍存在等等)並且資源管理器可以經常性的調用LooksAlive如果不想資源DLL被其打斷可以在Online例程中返回有效EventHandle(如前面所述)然後使用這個句柄向資源監視器通知資源狀態
IsAlive入口函數對資源狀態進行更詳細的計算並且由資源監視器進行(不能被阻止)資源DLL應該對資源做一個徹底的檢查看看它是否工作正常例如數據庫資源應該檢查數據庫是否能夠寫入到磁盤執行查詢和更新到磁盤等等
LooksAlive返回以下值
如果資源可能聯機並可用返回TRUE
如果資源不能正常工作LooksAlive返回FALSE
IsAlive返回以下值
如果資源聯機並且工作正常返回TRUE
如果資源不能正常工作返回FALSE
出於優化目的IsAlive入口函數應該在毫秒完成如前所述應該創建一個分離的工作線程來完成對資源的檢查工作該工作線程其後將狀態進行投遞以便於IsAlive能夠獲取並返回給資源管理器
注意LooksAlive在任何情況下不能超出毫秒大多數情況下應該不超過毫秒IsAlive稍長一些不過即使它是個異步調用資源監視器在同一線程中管理的其它資源入口函數將等待直接IsAlive返回派生線程並不能改善這種狀況因此IsAlive也應該在不超過毫秒的時間內完成
Offline例程
入口函數的討論到目前為止都是關於從核心開始定制一個表現良好的資源DLL下面入口函數為資源提供收尾和卸載機制第一個是Offline函數
Offline函數定義如下
DWORD WINAPI Offline
(
RESID ResourceId
);
ResourceId參數唯一標識了資源在使資源脫機時資源監視器調用這個入口函數一旦脫機對於群集客戶端該資源就不再可用
Offline返回以下值
如果成功的完成了資源脫機請求返回ERROR_SUCCESS
如果請求仍在等待並且一個線程已激活以處理脫機請求則返回ERROR_IO_PENDING
如果由於其它原因操作無法完成應該返回Win錯誤代碼
資源DLL應該在毫秒內優雅的關閉資源並從該入口返回如果Offline例程的實現超出了這個限制應該返回ERROR_IO_PENDING並派生一個線程來完成脫機請求該線程應該使用SetResourceStatus回調持續的向資源監視器更新資源狀態直到資源狀態為ClusterResourceOffline
如果資源沒有在PendingTimeout時間內優雅的關閉或Offline函數返回Win錯誤代碼資源監視器將調用Terminate入口函數來強制終止資源
Close例程
Close入口函數用於關閉資源並且對於一個資源只能調用一次應該使用Close翻譯由Open Offline ResourceControl或ResourceTypeControl入口函數分配的結構如果要關閉的資源仍沒有脫機調用Terminate強制使之脫機
Close例程定義如下
VOID WINAPI Close
(
RESID ResourceId
);
ResourcdId參數是被關閉資源的唯一標識
Close沒有返回值
資源DLL應該在毫秒內關閉資源不過如果超出了這個限制群集服務將以正當的方式處理
例程
Terminate入口函數立即終止一個在調用Offline時沒有優雅關閉的進程
Terminate例程定義如下
VOID WINAPI Terminate
(
RESID ResourceId
);
ResourceId是被強制脫機的資源的唯一標識如果資源DLL有線程正等待資源脫機或優雅的將資源脫機將放棄線程的脫機操作並強制使資源脫機
Terminate沒有返回值
ResourceControl和ResourceTypeControl例程
ResourceControl和ResourceTypeControl入口函數是可選的但是微軟建議實現這兩個函數以支持群集資源控制代碼管理工具如群集管理器和ClusterExe以及群集可感知應用程序使用ClusterResourceControl和ClusterResourceTypeControl函數與資源進行獨占的信息通信例如這些可用於設置屬性(公用和私有的)請求操作等等當管理器或群集可感知應用程序調用任一ClusterResourceXXXXControl函數資源管理器將分別調用ResourceControl或ResourceTypeControl將正確的控制代碼傳入實現了這兩個例程的資源DLL將根據控制代碼執行控制請求或設置資源屬性對於沒有處理的控制代碼資源DLL應該向資源監視器返回正確的狀態(ERROR_INVALID_FUNCTION狀態)在這種情況下如果有默認的處理動作資源監視器將執行之
ResourceControl例程定義如下
DWORD WINAPI ResourceControl
(
RESID ResourceId
DWORD ControlCode
LPVOID InBuffer
DWORD InBufferSize
LPVOID OutBuffer
DWORD OutBufferSize
LPDWORD BytesReturned
);
ResourceId參數是受影響資源的標識ControlCode代表要執行的操作的控制代碼該參數的有效值列表請參見平台SDK的Control Codes for Resources章節
InBuffer是該操作用到的傳入數據的緩沖區指針InBufferSize是它的大小以字節為單位OutBuffer是操作返回數據的緩沖區指針OutBufferSize是它的大小以字節為單位注意如果操作不需要數據或不返回數據InBuffer和OutBuffer可以為NULL
BytesReturned是OutBuffer中實際數據的字節數
ResourceControl返回以下值
如果ControlCode指定的操作成功完成返回ERROR_SUCCESS(不過實際返回有賴於控制代碼)
如果資源DLL不支持ControlCode指示的操作或者請求必須由資源監視器來處理則返回ERROR_INVALID_FUNCTION
如果操作不成功返回Win錯誤代碼
ResourceTypeControl例程定義如下
DWORD WINAPI ResourceTypeControl
(
LPCWSTR ResourceTypeName
DWORD ControlCode
LPVOID InBuffer
DWORD InBufferSize
LPVOID OutBuffer
DWORD OutBufferSize
LPDWORD BytesReturned
);
ResourceTypeName是操作所影響的資源類型的標識ControlCode為要執行的操作的控制代碼關於ControlCode參數有效值列表請參考平台SDK的Control Codes for Resources
InBuffer是該操作用到的傳入數據的緩沖區指針InBufferSize是它的大小以字節為單位OutBuffer是操作返回數據的緩沖區指針OutBufferSize是它的大小以字節為單位注意如果操作不需要數據或不返回數據InBuffer和OutBuffer可以為NULL
BytesReturned是在OutBuffer緩沖區中返回的結果數據的實際尺寸
ResourceTypeControl返回以下值
如果由ControlCode指示的操作成功完成ResourceTypeControl返回ERROR_SUCCESS(雖然實際的返回值有賴於控制代碼)
如果資源DLL不支持ControlCode指定的操作或者該請求必須由資源監視器來處理則返回ERROR_INVALID_FUNCTION
如果操作不成功ResourceTypeControl應該返回Win錯誤代碼
From:http://tw.wingwit.com/Article/program/net/201311/13753.html