Windows群集开发-编写微软群集服务器(MSCS)资源DLL:6)编写资源DLL

 
编写资源DLL
开始
可以使用MS Visual C++开发系统或其它C/C++开发工具编写自定义资源DLL。本文中例子使用的是:
  • Microsoft Visual C++ version 4.2b, 包含Unicode MFC库.
  • MIDL编译器,版本3.00.44。(该MIDL编译器可在SDK中找到)
  • 活动模板库(ATL)版本2.0(扩展例子代码和资源类型生成向导产生的代码需要它)
建立编译环境时,请参考平台SDK,特别是“Preparing a Build Environment”和“Developer Notes”章节。
在平台SDK的例子中也可找到完整的资源DLL参考实现(参见SMBsmp)。
创建新资源类型
要创建新的资源类型,必须写一个资源DLL和一个群集管理器扩展DLL。简单的方法是运行资源类型生成向导来创建资源DLL。该向导将箭竹一个资源DLL框架和/或包含入口点定义,申明,导出的群集管理器扩展DLL。
创建资源DLL的完整步骤,请参考SDK文档的“Creating a Custom Resource Type”,“Using 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拥有超过1个资源ID,就必须为DLL内所有共享的全局数据进行同步。
  • 在资源DLL内,一个入口点函数完成操作所花费的时间不应该超过300毫秒。如果一个入口点函数-特别是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入口点函数被调用后,如果它们要花费超过300毫秒,资源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
  • 如果操作不成功,返回Win32®编程接口的错误值。
出于对资源操作的优化,要确保实现的Startup能够在300毫秒内完成。
Open 例程
一旦Startup成功返回,典型地,资源监视器会为由资源DLL管理的每个资源调用Open入口点函数。
Open 例程的下定义如下:
RESID WINAPI Open(
 LPCWSTR ResourceName,
 HKEY ResourceKey,
 RESOURCE_HANDLE ResourceHandle
);
ResourceName 参数标识指定了被打开的资源(一个资源DLL能够提供给定类型的多个资源)。ResourceKey参数是关于资源的特定信息,是私有属性,并且,位于群集数据库中。该键值在Open返回时关闭;因此,如果该资源在其它入口点函数中访问一个键值,DLL应该调用ClusterRegOpenKey或ClusterRegCreateKey。ResourceHandle参数在SetResourceStatus和LogEvent回调函数中使用。
Open 使用群集API打开群集数据库并取得资源参数和私有属性。一个重要的事情是资源DLL应该在Open入口检查资源当前是否脱机(一个资源不能同时在超过一个节点上处于联机状态)。如果资源当前正联机,DLL应该尝试将其脱机。(注意 在这种情况下,online和offline指明了应用程序或服务的状态,而不是给定的群集节点-资源必须是真正脱机并且没有被节点所拥有。)另外,在Open例程中,资源DLL应该为资源创建专属的数据结构。
注意  如果资源为仲裁资源,则群集API在Open例程中是不可用的。
Open 返回以下值:
  • 如果操作成功,返回资源标识(RESID)。
  • 如果操作不成功,返回NULL。应该调用SetLastError指明发生的错误。
如果Open返回错误(返回NULL),资源将是不可管理的。因此,Open应该仅在相当稀有的情况返回错误(例如,不能为资源分配内存)。
出于对资源的优化操作,确保实现的Open将在300毫秒内完成。
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返回Win32错误值。
出于对资源操作的优化,确保Online例程的实现能够在300毫秒完成处理。如果做不到,应该在派生一个将资源联机的工作线程后立即返回ERROR_IO_PENDING给资源监视器,SetResourceStatus回调函数(该函数地址保存在Startup入口函数中)应该被周期性的调用,以指示资源状态。一旦资源联机,工作线程应该被跟中止或挂起以留待未来使用。
如果出于任何原因资源在联机时失败了,资源DLL应该使用LogEvent回调函数记录事件日志,并且应该调用SetResourceStatus函数。SetResourceStatus使用RESOURCE_STATUS结构来指示资源的联机或脱机状态。群集资源可以是下述状态之一:
  • 联机—状态代码ClusterResourceOnline
  • 脱机—状态代码ClusterResourceOffline
  • 失败—状态代码ClusterResourceFailed
  • 等待联机—状态代码ClusterResourceOnlinePending
  • 等待脱机—状态代码ClusterResourceOfflinePending
SDK. 关于SetResourceStatus的更多信息,请参考平台SDK。
如果,在3分钟后,资源仍没有联机,资源监视器将调用Terminate入口函数放弃操作。如果一个资源需要花费超过3分钟才能联机,使用群集API的群集管理器,Cluster.exe,或者其它管理工具可以使用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入口函数应该在300毫秒完成。如前所述,应该创建一个分离的工作线程来完成对资源的检查工作。该工作线程其后将状态进行投递以便于IsAlive能够获取并返回给资源管理器。
注意 LooksAlive 在任何情况下不能超出300毫秒。大多数情况下,应该不超过150毫秒。IsAlive稍长一些,不过即使它是个异步调用,资源监视器在同一线程中管理的其它资源入口函数将等待直接IsAlive返回。派生线程并不能改善这种状况。因此,IsAlive也应该在不超过300毫秒的时间内完成。
Offline 例程
入口函数的讨论到目前为止都是关于从核心开始定制一个表现良好的资源DLL。下面入口函数为资源提供收尾和卸载机制。第一个是Offline函数。
Offline 函数定义如下:
DWORD WINAPI Offline(
 RESID ResourceId
);
ResourceId 参数唯一标识了资源。在使资源脱机时,资源监视器调用这个入口函数。一旦脱机,对于群集客户端该资源就不再可用。
Offline 返回以下值:
  • 如果成功的完成了资源脱机请求,返回ERROR_SUCCESS。
  • 如果请求仍在等待并且一个线程已激活以处理脱机请求,则返回ERROR_IO_PENDING。
  • 如果由于其它原因,操作无法完成,应该返回Win32错误代码。
资源DLL应该在300毫秒内优雅的关闭资源并从该入口返回。如果Offline例程的实现超出了这个限制,应该返回ERROR_IO_PENDING,并派生一个线程来完成脱机请求。该线程应该使用SetResourceStatus回调持续的向资源监视器更新资源状态,直到资源状态为ClusterResourceOffline。
如果资源没有在PendingTimeout时间内优雅的关闭或Offline函数返回Win32错误代码,资源监视器将调用Terminate入口函数来强制终止资源。
Close 例程
Close 入口函数用于关闭资源,并且对于一个资源,只能调用一次。应该使用Close翻译由Open, Offline, ResourceControl或ResourceTypeControl入口函数分配的结构。如果要关闭的资源仍没有脱机,调用Terminate强制使之脱机。
Close 例程定义如下:
VOID WINAPI Close(
 RESID ResourceId 
);
ResourcdId 参数是被关闭资源的唯一标识。
Close 没有返回值。
资源DLL应该在300毫秒内关闭资源。不过,如果超出了这个限制,群集服务将以正当的方式处理。
例程
Terminate 入口函数立即终止一个在调用Offline时没有优雅关闭的进程。
Terminate 例程定义如下:
VOID WINAPI Terminate(
 RESID ResourceId
);
ResourceId 是被强制脱机的资源的唯一标识。如果资源DLL有线程正等待资源脱机或优雅的将资源脱机,将放弃线程的脱机操作,并强制使资源脱机。
Terminate 没有返回值。
ResourceControl 和ResourceTypeControl例程
ResourceControl 和ResourceTypeControl入口函数是可选的。但是,微软建议实现这两个函数以支持群集资源控制代码。管理工具,如群集管理器和Cluster.Exe,以及群集可感知应用程序使用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。
  • 如果操作不成功,返回Win32错误代码。
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应该返回Win32错误代码。

你可能感兴趣的:(windows,工作,服务器,dll,微软,winapi)