电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

Windows群集开发:6)编写资源DLL


发布日期:2020/2/17
 

编写资源DLL

开始

可以使用MS Visual C++开发系统或其它C/C++开发工具编写自定义资源DLL本文中例子使用的是

Microsoft Visual C++ version b 包含Unicode MFC库

MIDL编译器版本(该MIDL编译器可在SDK中找到)

活动模板库(ATL)版本(扩展例子代码和资源类型生成向导产生的代码需要它)

建立编译环境时请参考平台SDK特别是Preparing a Build EnvironmentDeveloper Notes章节

在平台SDK的例子中也可找到完整的资源DLL参考实现(参见SMBsmp)

创建新资源类型

要创建新的资源类型必须写一个资源DLL和一个群集管理器扩展DLL简单的方法是运行资源类型生成向导来创建资源DLL该向导将箭竹一个资源DLL框架和/或包含入口点定义申明导出的群集管理器扩展DLL

创建资源DLL的完整步骤请参考SDK文档的Creating a Custom Resource TypeUsing the Resource Type AppWizardCustomizing 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错误代码

上一篇:Win32 DLL的一个调试心得

下一篇:iframe并且控制不出现横向的滚动条的方法