/*++ Copyright (c) 1992-1998 Microsoft Corporation Module Name: genapp.c Abstract: Resource DLL for Generic Applications. Author: Rod Gamache (rodga) 8-Jan-1996 Revision History: --*/ #define UNICODE 1 #include "clusres.h" #include "clusrtl.h" #include "userenv.h" #define LOG_CURRENT_MODULE LOG_MODULE_GENAPP #define DBG_PRINT printf #define PARAM_NAME__COMMANDLINE CLUSREG_NAME_GENAPP_COMMAND_LINE #define PARAM_NAME__CURRENTDIRECTORY CLUSREG_NAME_GENAPP_CURRENT_DIRECTORY #define PARAM_NAME__USENETWORKNAME CLUSREG_NAME_GENAPP_USE_NETWORK_NAME #define PARAM_NAME__INTERACTWITHDESKTOP CLUSREG_NAME_GENAPP_INTERACT_WITH_DESKTOP #define PARAM_MIN__USENETWORKNAME 0 #define PARAM_MAX__USENETWORKNAME 1 #define PARAM_DEFAULT__USENETWORKNAME 0 #define PARAM_MIN__INTERACTWITHDESKTOP 0 #define PARAM_MAX__INTERACTWITHDESKTOP 1 #define PARAM_DEFAULT__INTERACTWITHDESKTOP 0 typedef struct _GENAPP_PARAMS { PWSTR CommandLine; PWSTR CurrentDirectory; DWORD UseNetworkName; DWORD InteractWithDesktop; } GENAPP_PARAMS, *PGENAPP_PARAMS; typedef struct _GENAPP_RESOURCE { GENAPP_PARAMS Params; HRESOURCE hResource; HANDLE hProcess; DWORD ProcessId; HKEY ResourceKey; HKEY ParametersKey; RESOURCE_HANDLE ResourceHandle; CLUS_WORKER PendingThread; BOOL Online; BOOL SentCloseMessage; } GENAPP_RESOURCE, *PGENAPP_RESOURCE; // // Global Data // RESUTIL_PROPERTY_ITEM GenAppResourcePrivateProperties[] = { { PARAM_NAME__COMMANDLINE, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(GENAPP_PARAMS,CommandLine) }, { PARAM_NAME__CURRENTDIRECTORY, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(GENAPP_PARAMS,CurrentDirectory) }, { PARAM_NAME__INTERACTWITHDESKTOP, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__INTERACTWITHDESKTOP, PARAM_MIN__INTERACTWITHDESKTOP, PARAM_MAX__INTERACTWITHDESKTOP, 0, FIELD_OFFSET(GENAPP_PARAMS,InteractWithDesktop) }, { PARAM_NAME__USENETWORKNAME, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__USENETWORKNAME, PARAM_MIN__USENETWORKNAME, PARAM_MAX__USENETWORKNAME, 0, FIELD_OFFSET(GENAPP_PARAMS,UseNetworkName) }, { 0 } }; // // critsec to synchronize calling of SetProcessWindowStation in ClRtl routine // CRITICAL_SECTION GenAppWinsta0Lock; // Event Logging routine #define g_LogEvent ClusResLogEvent #define g_SetResourceStatus ClusResSetResourceStatus // Forward reference to our RESAPI function table. extern CLRES_FUNCTION_TABLE GenAppFunctionTable; // // Forward routines // BOOLEAN VerifyApp( IN RESID ResourceId, IN BOOLEAN IsAliveFlag ); BOOL FindOurWindow( HWND WindowHandle, LPARAM OurProcessId ); DWORD GenAppGetPrivateResProperties( IN OUT PGENAPP_RESOURCE ResourceEntry, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ); DWORD GenAppValidatePrivateResProperties( IN OUT PGENAPP_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PGENAPP_PARAMS Params ); DWORD GenAppSetPrivateResProperties( IN OUT PGENAPP_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize ); DWORD GenAppGetPids( IN OUT PGENAPP_RESOURCE ResourceEntry, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ); // // end of forward declarations // BOOL GenAppInit( VOID ) { BOOL success; DWORD spinCount; // // set spinCount so system pre-allocates the event for critical // sections. use the same spin count that the heap mgr uses as doc'ed in // MSDN // spinCount = 0x80000000 | 4000; success = InitializeCriticalSectionAndSpinCount(&GenAppWinsta0Lock, spinCount); return success; } VOID GenAppUninit( VOID ) { DeleteCriticalSection( &GenAppWinsta0Lock ); } BOOLEAN WINAPI GenAppDllEntryPoint( IN HINSTANCE DllHandle, IN DWORD Reason, IN LPVOID Reserved ) { switch( Reason ) { case DLL_PROCESS_ATTACH: if ( !GenAppInit() ) { return(FALSE); } break; case DLL_PROCESS_DETACH: GenAppUninit(); break; default: break; } return(TRUE); } // GenAppDllEntryPoint RESID WINAPI GenAppOpen( IN LPCWSTR ResourceName, IN HKEY ResourceKey, IN RESOURCE_HANDLE ResourceHandle ) /*++ Routine Description: Open routine for generic application resource. Arguments: ResourceName - supplies the resource name ResourceKey - supplies a handle to the resource's cluster registry key ResourceHandle - the resource handle to be supplied with SetResourceStatus is called. Return Value: RESID of created resource Zero on failure --*/ { RESID appResid = 0; DWORD errorCode; HKEY parametersKey = NULL; HKEY resKey = NULL; PGENAPP_RESOURCE resourceEntry = NULL; DWORD paramNameMaxSize = 0; HCLUSTER hCluster; // // Get registry parameters for this resource. // errorCode = ClusterRegOpenKey( ResourceKey, CLUSREG_KEYNAME_PARAMETERS, KEY_READ, ¶metersKey ); if ( errorCode != NO_ERROR ) { (g_LogEvent)(ResourceHandle, LOG_ERROR, L"Unable to open parameters key. Error: %1!u!.\n", errorCode ); goto error_exit; } // // Get a handle to our resource key so that we can get our name later // if we need to log an event. // errorCode = ClusterRegOpenKey( ResourceKey, L"", KEY_READ, &resKey); if (errorCode != ERROR_SUCCESS) { (g_LogEvent)(ResourceHandle, LOG_ERROR, L"Unable to open resource key. Error: %1!u!.\n", errorCode ); goto error_exit; } resourceEntry = LocalAlloc( LMEM_FIXED, sizeof(GENAPP_RESOURCE) ); if ( resourceEntry == NULL ) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to allocate a process info structure.\n" ); errorCode = ERROR_NOT_ENOUGH_MEMORY; goto error_exit; } ZeroMemory( resourceEntry, sizeof(GENAPP_RESOURCE) ); resourceEntry->ResourceHandle = ResourceHandle; resourceEntry->ResourceKey = resKey; resourceEntry->ParametersKey = parametersKey; hCluster = OpenCluster(NULL); if (hCluster == NULL) { errorCode = GetLastError(); (g_LogEvent)(ResourceHandle, LOG_ERROR, L"Failed to open cluster, error %1!u!.\n", errorCode); goto error_exit; } resourceEntry->hResource = OpenClusterResource( hCluster, ResourceName ); errorCode = GetLastError(); CloseCluster(hCluster); if (resourceEntry->hResource == NULL) { (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to open resource, error %1!u!.\n", errorCode ); goto error_exit; } appResid = (RESID)resourceEntry; error_exit: if ( appResid == NULL) { if (parametersKey != NULL) { ClusterRegCloseKey( parametersKey ); } if (resKey != NULL) { ClusterRegCloseKey( resKey ); } } if ( (appResid == 0) && (resourceEntry != NULL) ) { LocalFree( resourceEntry ); } if ( errorCode != ERROR_SUCCESS ) { SetLastError( errorCode ); } return(appResid); } // GenAppOpen DWORD WINAPI GenAppOnlineWorker( IN PCLUS_WORKER Worker, IN PGENAPP_RESOURCE ResourceEntry ) /*++ Routine Description: Does the work of bringing a genapp resource online. Arguments: Worker - Supplies the worker structure ResourceEntry - A pointer to the GenApp block for this resource. Returns: ERROR_SUCCESS if successful. Win32 error code on failure. --*/ { RESOURCE_STATUS resourceStatus; DWORD status = ERROR_SUCCESS; STARTUPINFO StartupInfo; PROCESS_INFORMATION Process; LPWSTR nameOfPropInError; LPWSTR expandedDir = NULL; LPWSTR expandedCommand = NULL; // Create Process parameters LPVOID Environment = NULL; LPVOID OldEnvironment; ResUtilInitializeResourceStatus( &resourceStatus ); resourceStatus.ResourceState = ClusterResourceFailed; //resourceStatus.WaitHint = 0; resourceStatus.CheckPoint = 1; // // Read our parameters. // status = ResUtilGetPropertiesToParameterBlock( ResourceEntry->ParametersKey, GenAppResourcePrivateProperties, (LPBYTE) &ResourceEntry->Params, TRUE, // CheckForRequiredProperties &nameOfPropInError ); if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to read the '%1' property. Error: %2!u!.\n", (nameOfPropInError == NULL ? L"" : nameOfPropInError), status ); goto error_exit; } if ( ResourceEntry->Params.UseNetworkName ) { // // Create the new environment with the simulated net name. // Environment = ResUtilGetEnvironmentWithNetName( ResourceEntry->hResource ); } else { HANDLE processToken; // // get the current process token. If it fails, we revert to using just the // system environment area // OpenProcessToken( GetCurrentProcess(), MAXIMUM_ALLOWED, &processToken ); // // Clone the current environment, picking up any changes that might have // been made after resmon started // CreateEnvironmentBlock(&Environment, processToken, FALSE ); if ( processToken != NULL ) { CloseHandle( processToken ); } } ZeroMemory( &StartupInfo, sizeof(StartupInfo) ); StartupInfo.cb = sizeof(StartupInfo); //StartupInfo.lpTitle = NULL; //StartupInfo.lpDesktop = NULL; StartupInfo.wShowWindow = SW_HIDE; StartupInfo.dwFlags = STARTF_USESHOWWINDOW; if ( ResourceEntry->Params.InteractWithDesktop ) { // // don't blindly hang waiting for the lock to become available. // while ( !TryEnterCriticalSection( &GenAppWinsta0Lock )) { if ( ClusWorkerCheckTerminate( Worker )) { (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING, L"Aborting online due to worker thread terminate request. lock currently " L"owned by thread %1!u!.\n", GenAppWinsta0Lock.OwningThread ); goto error_exit; } Sleep( 1000 ); } status = ClRtlAddClusterServiceAccountToWinsta0DACL(); LeaveCriticalSection( &GenAppWinsta0Lock ); if ( status != ERROR_SUCCESS ) { (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to set DACL on interactive window station and its desktop. Error: %1!u!.\n", status ); goto error_exit; } StartupInfo.lpDesktop = L"WinSta0\\Default"; StartupInfo.wShowWindow = SW_SHOW; } // // Expand the current directory parameter // if ( ResourceEntry->Params.CurrentDirectory ) { expandedDir = ResUtilExpandEnvironmentStrings( ResourceEntry->Params.CurrentDirectory ); if ( expandedDir == NULL ) { status = GetLastError(); (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"Error expanding the current directory, %1!ls!. Error: %2!u!.\n", ResourceEntry->Params.CurrentDirectory, status ); goto error_exit; } } // // Expand the command line parameter // if ( ResourceEntry->Params.CommandLine ) { expandedCommand = ResUtilExpandEnvironmentStrings( ResourceEntry->Params.CommandLine ); if ( expandedCommand == NULL ) { status = GetLastError(); (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"Error expanding the command line, %1!ls!. Error: %2!u!.\n", ResourceEntry->Params.CommandLine, status ); goto error_exit; } } if ( !CreateProcess( NULL, expandedCommand, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, Environment, expandedDir, &StartupInfo, &Process ) ) { status = GetLastError(); ClusResLogSystemEventByKeyData(ResourceEntry->ResourceKey, LOG_CRITICAL, RES_GENAPP_CREATE_FAILED, sizeof(status), &status); (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to create process. Error: %1!u!.\n", status ); goto error_exit; } // // Save the handle to the process // ResourceEntry->hProcess = Process.hProcess; ResourceEntry->ProcessId = Process.dwProcessId; CloseHandle( Process.hThread ); ResourceEntry->Online = TRUE; // // When the process fails EventHandle will be signaled. // Because of this no polling is necessary. // resourceStatus.EventHandle = ResourceEntry->hProcess; resourceStatus.ResourceState = ClusterResourceOnline; error_exit: (g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus ); if ( resourceStatus.ResourceState == ClusterResourceOnline ) { ResourceEntry->Online = TRUE; } else { ResourceEntry->Online = FALSE; } if ( expandedDir != NULL ) { LocalFree( expandedDir ); } if ( expandedCommand != NULL ) { LocalFree( expandedCommand ); } if (Environment != NULL) { RtlDestroyEnvironment(Environment); } return(status); } // GenAppOnlineWorker DWORD WINAPI GenAppOnline( IN RESID ResourceId, IN OUT PHANDLE EventHandle ) /*++ Routine Description: Online routine for Generic Application resource. Arguments: ResourceId - Supplies resource id to be brought online EventHandle - Supplies a pointer to a handle to signal on error. Return Value: ERROR_SUCCESS if successful. ERROR_RESOURCE_NOT_FOUND if RESID is not valid. ERROR_RESOURCE_NOT_AVAILABLE if resource was arbitrated but failed to acquire 'ownership'. Win32 error code if other failure. --*/ { PGENAPP_RESOURCE resourceEntry; DWORD status = ERROR_SUCCESS; resourceEntry = (PGENAPP_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { #if DBG OutputDebugStringA( "GenApp: Online request for a nonexistent resource\n" ); #endif return(ERROR_RESOURCE_NOT_FOUND); } if ( resourceEntry->hProcess != NULL ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Online request and process handle is not NULL!\n" ); return(ERROR_NOT_READY); } ClusWorkerTerminate( &resourceEntry->PendingThread ); status = ClusWorkerCreate( &resourceEntry->PendingThread, GenAppOnlineWorker, resourceEntry ); if ( status == ERROR_SUCCESS ) { status = ERROR_IO_PENDING; } return(status); } // GenAppOnline VOID WINAPI GenAppTerminate( IN RESID ResourceId ) /*++ Routine Description: Terminate entry point for the Generic Application resource. Arguments: ResourceId - Supplies resource id to be terminated Return Value: None. --*/ { PGENAPP_RESOURCE pResource; DWORD errorCode; pResource = ( PGENAPP_RESOURCE ) ResourceId; // // synchronize with any existing pending operation // ClusWorkerTerminate( &pResource->PendingThread ); if ( pResource->hProcess != NULL ) { if ( !TerminateProcess( pResource->hProcess, 1 ) ) { errorCode = GetLastError(); if ( errorCode != ERROR_ACCESS_DENIED ) { (g_LogEvent)(pResource->ResourceHandle, LOG_ERROR, L"Failed to terminate Process ID %1!u!. Error: %2!u!.\n", pResource->ProcessId, errorCode ); } } pResource->ProcessId = 0; CloseHandle( pResource->hProcess ); pResource->hProcess = NULL; pResource->Online = FALSE; } } // GenAppTerminate DWORD WINAPI GenAppOfflineWorker( IN PCLUS_WORKER Worker, IN PGENAPP_RESOURCE ResourceEntry ) /*++ Routine Description: Real worker routine for offlining a Generic Application resource. Arguments: Worker - Supplies the worker structure Context - A pointer to the GenApp block for this resource. Return Value: None. --*/ { DWORD errorCode = ERROR_SUCCESS; BOOL switchedDesktop = FALSE; HDESK previousDesktop = NULL; HDESK inputDesktop; HDESK desktopHandle = NULL; BOOL success; BOOL callTerminateProc = TRUE; HWINSTA winsta0 = NULL; HWINSTA previousWinsta; DWORD logLevel = LOG_INFORMATION; RESOURCE_STATUS resourceStatus; // // init resource status structure // ResUtilInitializeResourceStatus( &resourceStatus ); resourceStatus.ResourceState = ClusterResourceFailed; resourceStatus.CheckPoint = 1; // // get a handle to the appropriate desktop so we enum the correct window // set. // if ( ResourceEntry->Params.InteractWithDesktop ) { // // periodically check to see if we should terminate // while ( !TryEnterCriticalSection( &GenAppWinsta0Lock )) { if ( ClusWorkerCheckTerminate( Worker )) { (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING, L"Aborting offline while trying to acquire desktop lock. lock currently " L"owned by thread %1!u!.\n", GenAppWinsta0Lock.OwningThread ); goto error_exit; } Sleep( 500 ); } winsta0 = OpenWindowStation( L"winsta0", FALSE, GENERIC_ALL ); if ( winsta0 != NULL ) { previousWinsta = GetProcessWindowStation(); if ( previousWinsta != NULL ) { success = SetProcessWindowStation( winsta0 ); if ( success ) { // // if we have window station access, we should have desktop as well // desktopHandle = OpenInputDesktop( 0, FALSE, GENERIC_ALL ); if ( desktopHandle != NULL ) { switchedDesktop = TRUE; } } } } if ( !switchedDesktop ) { errorCode = GetLastError(); (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to switch to interactive desktop for process %1!u!, status %2!u!.\n", ResourceEntry->ProcessId, errorCode ); LeaveCriticalSection( &GenAppWinsta0Lock ); if ( winsta0 != NULL ) { CloseWindowStation( winsta0 ); } } } else { // // get handle to service controller's desktop // desktopHandle = GetThreadDesktop( GetCurrentThreadId() ); } ResourceEntry->SentCloseMessage = FALSE; #if DBG (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Looking for window with PID %1!u!.\n", ResourceEntry->ProcessId ); #endif // // find our window. If found, we'll post a WM_CLOSE and set // SentCloseMessage. It's possible, under heavy load conditions with rapid // resource state transition, that the primary top level window associated // with the process has not been created when the offline request is // made. We'll send WM_CLOSE to the ones we find and hope that it shuts // down the process. If no windows are found, we just terminate the // process. // do { if ( desktopHandle ) { EnumDesktopWindows( desktopHandle, FindOurWindow, (LPARAM)ResourceEntry ); } if ( ResourceEntry->SentCloseMessage ) { // // we found a toplevel window. wait on the process handle until the // handle is signalled or a pending timeout has occurred // (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Sent WM_CLOSE to process %1!u! - waiting for process to terminate.\n", ResourceEntry->ProcessId); errorCode = WaitForSingleObject( ResourceEntry->hProcess, 1000 ); if ( errorCode == WAIT_OBJECT_0 ) { callTerminateProc = FALSE; break; } else { if ( ClusWorkerCheckTerminate( Worker )) { (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_WARNING, L"Aborting offline while waiting for process to terminate.\n"); logLevel = LOG_WARNING; break; } } } else { (g_LogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION, L"No windows detected for process %1!u!\n", ResourceEntry->ProcessId); break; } } while ( TRUE ); if ( switchedDesktop ) { SetProcessWindowStation( previousWinsta ); CloseDesktop( desktopHandle ); CloseWindowStation( winsta0 ); LeaveCriticalSection( &GenAppWinsta0Lock ); } if ( callTerminateProc ) { (g_LogEvent)(ResourceEntry->ResourceHandle, logLevel, L"Terminating process %1!u!\n", ResourceEntry->ProcessId); if ( !TerminateProcess( ResourceEntry->hProcess, 1 ) ) { errorCode = GetLastError(); if ( errorCode != ERROR_ACCESS_DENIED ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to terminate Process ID %1!u!. Error: %2!u!.\n", ResourceEntry->ProcessId, errorCode ); } } } ResourceEntry->ProcessId = 0; CloseHandle( ResourceEntry->hProcess ); ResourceEntry->hProcess = NULL; ResourceEntry->Online = FALSE; resourceStatus.ResourceState = ClusterResourceOffline; error_exit: (g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus ); return ERROR_SUCCESS; } // GenAppOfflineThread DWORD WINAPI GenAppOffline( IN RESID ResourceId ) /*++ Routine Description: Offline routine for Generic Application resource. Arguments: ResourceId - Supplies the resource to be taken offline Return Value: ERROR_SUCCESS - always successful. --*/ { PGENAPP_RESOURCE resourceEntry; DWORD status = ERROR_SUCCESS; resourceEntry = (PGENAPP_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { #if DBG OutputDebugStringA( "GenApp: Offline request for a nonexistent resource\n" ); #endif return(ERROR_RESOURCE_NOT_FOUND); } if ( resourceEntry->hProcess == NULL ) { (g_LogEvent)( resourceEntry->ResourceHandle, LOG_ERROR, L"Offline request and process handle is NULL!\n" ); return(ERROR_NOT_READY); } ClusWorkerTerminate( &resourceEntry->PendingThread ); status = ClusWorkerCreate( &resourceEntry->PendingThread, GenAppOfflineWorker, resourceEntry ); if ( status == ERROR_SUCCESS ) { status = ERROR_IO_PENDING; } return(status); } // GenAppOffline BOOL WINAPI GenAppIsAlive( IN RESID ResourceId ) /*++ Routine Description: IsAlive routine for Generice Applications resource. Arguments: ResourceId - Supplies the resource id to be polled. Return Value: TRUE - Resource is alive and well FALSE - Resource is toast. --*/ { return VerifyApp( ResourceId, TRUE ); } // GenAppIsAlive BOOLEAN VerifyApp( IN RESID ResourceId, IN BOOLEAN IsAliveFlag ) /*++ Routine Description: Verify that a Generic Applications resource is running Arguments: ResourceId - Supplies the resource id to be polled. IsAliveFlag - TRUE if called from IsAlive, otherwise called from LooksAlive. Return Value: TRUE - Resource is alive and well FALSE - Resource is toast. --*/ { return TRUE; } // VerifyApp BOOL WINAPI GenAppLooksAlive( IN RESID ResourceId ) /*++ Routine Description: LooksAlive routine for Generic Applications resource. Arguments: ResourceId - Supplies the resource id to be polled. Return Value: TRUE - Resource looks like it is alive and well FALSE - Resource looks like it is toast. --*/ { return VerifyApp( ResourceId, FALSE ); } // GenAppLooksAlive VOID WINAPI GenAppClose( IN RESID ResourceId ) /*++ Routine Description: Close routine for Generic Applications resource. Arguments: ResourceId - Supplies resource id to be closed Return Value: None. --*/ { PGENAPP_RESOURCE resourceEntry; DWORD errorCode; resourceEntry = (PGENAPP_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { #if DBG OutputDebugStringA( "GenApp: Close request for a nonexistent resource\n" ); #endif return; } ClusterRegCloseKey( resourceEntry->ParametersKey ); ClusterRegCloseKey( resourceEntry->ResourceKey ); CloseClusterResource( resourceEntry->hResource ); LocalFree( resourceEntry->Params.CommandLine ); LocalFree( resourceEntry->Params.CurrentDirectory ); LocalFree( resourceEntry ); } // GenAppClose DWORD GenAppResourceControl( IN RESID ResourceId, IN DWORD ControlCode, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) /*++ Routine Description: ResourceControl routine for Generic Application resources. Perform the control request specified by ControlCode on the specified resource. Arguments: ResourceId - Supplies the resource id for the specific resource. ControlCode - Supplies the control code that defines the action to be performed. InBuffer - Supplies a pointer to a buffer containing input data. InBufferSize - Supplies the size, in bytes, of the data pointed to by InBuffer. OutBuffer - Supplies a pointer to the output buffer to be filled in. OutBufferSize - Supplies the size, in bytes, of the available space pointed to by OutBuffer. BytesReturned - Returns the number of bytes of OutBuffer actually filled in by the resource. If OutBuffer is too small, BytesReturned contains the total number of bytes for the operation to succeed. Return Value: ERROR_SUCCESS - The function completed successfully. ERROR_INVALID_FUNCTION - The requested control code is not supported. In some cases, this allows the cluster software to perform the work. Win32 error code - The function failed. --*/ { DWORD status; PGENAPP_RESOURCE resourceEntry; DWORD required; resourceEntry = (PGENAPP_RESOURCE)ResourceId; if ( resourceEntry == NULL ) { #if DBG OutputDebugStringA( "GenApp: ResourceControl request for a nonexistent resource\n" ); #endif return(FALSE); } switch ( ControlCode ) { case CLUSCTL_RESOURCE_UNKNOWN: *BytesReturned = 0; status = ERROR_SUCCESS; break; case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTY_FMTS: status = ResUtilGetPropertyFormats( GenAppResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_ENUM_PRIVATE_PROPERTIES: status = ResUtilEnumProperties( GenAppResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES: status = GenAppGetPrivateResProperties( resourceEntry, OutBuffer, OutBufferSize, BytesReturned ); break; case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES: status = GenAppValidatePrivateResProperties( resourceEntry, InBuffer, InBufferSize, NULL ); break; case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES: status = GenAppSetPrivateResProperties( resourceEntry, InBuffer, InBufferSize ); break; case CLUSCTL_RESOURCE_GET_LOADBAL_PROCESS_LIST: status = GenAppGetPids( resourceEntry, OutBuffer, OutBufferSize, BytesReturned ); break; default: status = ERROR_INVALID_FUNCTION; break; } return(status); } // GenAppResourceControl DWORD GenAppResourceTypeControl( IN LPCWSTR ResourceTypeName, IN DWORD ControlCode, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) /*++ Routine Description: ResourceTypeControl routine for Generic Application resources. Perform the control request specified by ControlCode for this resource type. Arguments: ResourceTypeName - Supplies the resource type name. ControlCode - Supplies the control code that defines the action to be performed. InBuffer - Supplies a pointer to a buffer containing input data. InBufferSize - Supplies the size, in bytes, of the data pointed to by InBuffer. OutBuffer - Supplies a pointer to the output buffer to be filled in. OutBufferSize - Supplies the size, in bytes, of the available space pointed to by OutBuffer. BytesReturned - Returns the number of bytes of OutBuffer actually filled in by the resource. If OutBuffer is too small, BytesReturned contains the total number of bytes for the operation to succeed. Return Value: ERROR_SUCCESS - The function completed successfully. ERROR_INVALID_FUNCTION - The requested control code is not supported. In some cases, this allows the cluster software to perform the work. Win32 error code - The function failed. --*/ { DWORD status; DWORD required; switch ( ControlCode ) { case CLUSCTL_RESOURCE_TYPE_UNKNOWN: *BytesReturned = 0; status = ERROR_SUCCESS; break; case CLUSCTL_RESOURCE_TYPE_GET_PRIVATE_RESOURCE_PROPERTY_FMTS: status = ResUtilGetPropertyFormats( GenAppResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES: status = ResUtilEnumProperties( GenAppResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } break; default: status = ERROR_INVALID_FUNCTION; break; } return(status); } // GenAppResourceTypeControl DWORD GenAppGetPrivateResProperties( IN OUT PGENAPP_RESOURCE ResourceEntry, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) /*++ Routine Description: Processes the CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES control function for resources of type GenApp. Arguments: ResourceEntry - Supplies the resource entry on which to operate. OutBuffer - Returns the output data. OutBufferSize - Supplies the size, in bytes, of the data pointed to by OutBuffer. BytesReturned - The number of bytes returned in OutBuffer. Return Value: ERROR_SUCCESS - The function completed successfully. ERROR_INVALID_PARAMETER - The data is formatted incorrectly. ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory. Win32 error code - The function failed. --*/ { DWORD status; DWORD required; status = ResUtilGetAllProperties( ResourceEntry->ParametersKey, GenAppResourcePrivateProperties, OutBuffer, OutBufferSize, BytesReturned, &required ); if ( status == ERROR_MORE_DATA ) { *BytesReturned = required; } return(status); } // GenAppGetPrivateResProperties DWORD GenAppValidatePrivateResProperties( IN OUT PGENAPP_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize, OUT PGENAPP_PARAMS Params ) /*++ Routine Description: Processes the CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES control function for resources of type Generic Application. Arguments: ResourceEntry - Supplies the resource entry on which to operate. InBuffer - Supplies a pointer to a buffer containing input data. InBufferSize - Supplies the size, in bytes, of the data pointed to by InBuffer. Params - Supplies the parameter block to fill in. Return Value: ERROR_SUCCESS - The function completed successfully. ERROR_INVALID_PARAMETER - The data is formatted incorrectly. ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory. ERROR_DEPENDENCY_NOT_FOUND - Trying to set UseNetworkName when there is no dependency on a Network Name resource. Win32 error code - The function failed. --*/ { DWORD status; GENAPP_PARAMS currentProps; GENAPP_PARAMS newProps; PGENAPP_PARAMS pParams = NULL; BOOL hResDependency; LPWSTR nameOfPropInError; WCHAR netnameBuffer[ MAX_PATH + 1 ]; DWORD netnameBufferSize = sizeof( netnameBuffer ) / sizeof( WCHAR ); // // Check if there is input data. // if ( (InBuffer == NULL) || (InBufferSize < sizeof(DWORD)) ) { return(ERROR_INVALID_DATA); } // // Retrieve the current set of private properties from the // cluster database. // ZeroMemory( ¤tProps, sizeof(currentProps) ); status = ResUtilGetPropertiesToParameterBlock( ResourceEntry->ParametersKey, GenAppResourcePrivateProperties, (LPBYTE) ¤tProps, FALSE, /*CheckForRequiredProperties*/ &nameOfPropInError ); if ( status != ERROR_SUCCESS ) { (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Unable to read the '%1' property. Error: %2!u!.\n", (nameOfPropInError == NULL ? L"" : nameOfPropInError), status ); goto FnExit; } // // Duplicate the resource parameter block. // if ( Params == NULL ) { pParams = &newProps; } else { pParams = Params; } ZeroMemory( pParams, sizeof(GENAPP_PARAMS) ); status = ResUtilDupParameterBlock( (LPBYTE) pParams, (LPBYTE) ¤tProps, GenAppResourcePrivateProperties ); if ( status != ERROR_SUCCESS ) { return(status); } // // Parse and validate the properties. // status = ResUtilVerifyPropertyTable( GenAppResourcePrivateProperties, NULL, TRUE, // Allow unknowns InBuffer, InBufferSize, (LPBYTE) pParams ); if ( status == ERROR_SUCCESS ) { // // Validate the CurrentDirectory // if ( pParams->CurrentDirectory && !ResUtilIsPathValid( pParams->CurrentDirectory ) ) { status = ERROR_INVALID_PARAMETER; goto FnExit; } // // If the resource should use the network name as the computer // name, make sure there is a dependency on a Network Name // resource. // if ( pParams->UseNetworkName ) { hResDependency = GetClusterResourceNetworkName(ResourceEntry->hResource, netnameBuffer, &netnameBufferSize); if ( !hResDependency ) { status = ERROR_DEPENDENCY_NOT_FOUND; } } } FnExit: // // Cleanup our parameter block. // if ( ( (status != ERROR_SUCCESS) && (pParams != NULL) ) || ( pParams == &newProps ) ) { ResUtilFreeParameterBlock( (LPBYTE) pParams, (LPBYTE) ¤tProps, GenAppResourcePrivateProperties ); } ResUtilFreeParameterBlock( (LPBYTE) ¤tProps, NULL, GenAppResourcePrivateProperties ); return(status); } // GenAppValidatePrivateResProperties DWORD GenAppSetPrivateResProperties( IN OUT PGENAPP_RESOURCE ResourceEntry, IN PVOID InBuffer, IN DWORD InBufferSize ) /*++ Routine Description: Processes the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control function for resources of type Generic Application. Arguments: ResourceEntry - Supplies the resource entry on which to operate. InBuffer - Supplies a pointer to a buffer containing input data. InBufferSize - Supplies the size, in bytes, of the data pointed to by InBuffer. Return Value: ERROR_SUCCESS - The function completed successfully. ERROR_INVALID_PARAMETER - The data is formatted incorrectly. ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory. Win32 error code - The function failed. --*/ { DWORD status; GENAPP_PARAMS params; ZeroMemory( ¶ms, sizeof(GENAPP_PARAMS) ); // // Parse and validate the properties. // status = GenAppValidatePrivateResProperties( ResourceEntry, InBuffer, InBufferSize, ¶ms ); if ( status != ERROR_SUCCESS ) { return(status); } // // Save the parameter values. // status = ResUtilSetPropertyParameterBlock( ResourceEntry->ParametersKey, GenAppResourcePrivateProperties, NULL, (LPBYTE) ¶ms, InBuffer, InBufferSize, (LPBYTE) &ResourceEntry->Params ); ResUtilFreeParameterBlock( (LPBYTE) ¶ms, (LPBYTE) &ResourceEntry->Params, GenAppResourcePrivateProperties ); // // If the resource is online, return a non-success status. // if (status == ERROR_SUCCESS) { if ( ResourceEntry->Online ) { status = ERROR_RESOURCE_PROPERTIES_STORED; } else { status = ERROR_SUCCESS; } } return status; } // GenAppSetPrivateResProperties DWORD GenAppGetPids( IN OUT PGENAPP_RESOURCE ResourceEntry, OUT PVOID OutBuffer, IN DWORD OutBufferSize, OUT LPDWORD BytesReturned ) /*++ Routine Description: Get array of PIDs (as DWORDS) to return for load balancing purposes. Arguments: ResourceEntry - Supplies the resource entry on which to operate. OutBuffer - Supplies a pointer to a buffer for output data. OutBufferSize - Supplies the size, in bytes, of the buffer pointed to by OutBuffer. BytesReturned - The number of bytes returned in OutBuffer. Return Value: ERROR_SUCCESS if successful. A Win32 error code on failure. --*/ { CLUSPROP_BUFFER_HELPER props; props.pb = OutBuffer; *BytesReturned = sizeof(*props.pdw); if ( OutBufferSize < sizeof(*props.pdw) ) { return(ERROR_MORE_DATA); } *(props.pdw) = ResourceEntry->ProcessId; return(ERROR_SUCCESS); } // GenAppGetPids BOOL FindOurWindow( HWND WindowHandle, LPARAM Resource ) /*++ Routine Description: Find our window handle in the midst of all of this confusion. Arguments: WindowHandle - a handle to the current window being enumerated. Resource - pointer to resource's context block Return Value: TRUE - if we should continue enumeration. FALSE - if we should not continue enumeration. --*/ { DWORD windowPid; BOOL success; PGENAPP_RESOURCE resource = (PGENAPP_RESOURCE)Resource; GetWindowThreadProcessId( WindowHandle, &windowPid ); if ( windowPid == resource->ProcessId ) { success = PostMessage(WindowHandle, WM_CLOSE, 0, 0); if ( success ) { #if DBG if ( !resource->SentCloseMessage ) { (g_LogEvent)(resource->ResourceHandle, LOG_ERROR, L"Posting WM_CLOSE message on HWND %2!08X! for process %1!u!\n", resource->ProcessId, WindowHandle); } else { (g_LogEvent)(resource->ResourceHandle, LOG_ERROR, L"Posting WM_CLOSE message on addt'l HWND %2!08X! for process %1!u!\n", resource->ProcessId, WindowHandle); } #endif resource->SentCloseMessage = TRUE; } else { (g_LogEvent)(resource->ResourceHandle, LOG_ERROR, L"Posting of WM_CLOSE message to window for process %1!u! failed - status %2!u!\n", resource->ProcessId, GetLastError()); } } // // continue to enum since a process can have more than one top level window // return TRUE; } // FindOurWindow //*********************************************************** // // Define Function Table // //*********************************************************** CLRES_V1_FUNCTION_TABLE( GenAppFunctionTable, // Name CLRES_VERSION_V1_00, // Version GenApp, // Prefix NULL, // Arbitrate NULL, // Release GenAppResourceControl,// ResControl GenAppResourceTypeControl ); // ResTypeControl