#define INITGUID #include #include #include #include #include #include #include #include "pwstray.h" #include "resource.h" #include "sink.h" #include #define MB_TIMEOUT 5000 // not a string! #define DW_TRAY_ICON_ID 'PWSt' #define REGKEY_STP _T("SOFTWARE\\Microsoft\\INetStp") #define REGKEY_INSTALLKEY _T("InstallPath") //USE_MFC=1 //========================================================= globals HINSTANCE g_hInstance = NULL; HWND g_hwnd = NULL; HMENU g_hMenuMain = NULL; UINT g_dwNewTaskbarMessage = 0; DWORD g_dwSinkCookie = 0; CImpIMSAdminBaseSink* g_pEventSink = NULL; IConnectionPoint* g_pConnPoint = NULL; IMSAdminBase* g_pMBCom = NULL; DWORD g_dwServerState = 0; //========================================================= forwards DWORD DWGetServerState(); BOOL FUpdateTrayIcon( DWORD dwMessage ); BOOL InitializeMetabase( OLECHAR* pocMachineName ); void TerminateMetabase(); BOOL InitializeSink(); void TerminateSink(); BOOL GetAdminPath( TCHAR* pch, WORD cch ); BOOL SetServerState( DWORD dwControlCode ); BOOL LaunchAdminUI(); void RunContextMenu(); BOOL FIsW3Running(); void CheckIfServerIsRunningAgain(); void CheckIfServerUpAndDied(); // routine to see if w3svc is running //-------------------------------------------------------------------- // the method we use to see if the service is running is different on // windows NT from win95 BOOL FIsW3Running() { OSVERSIONINFO info_os; info_os.dwOSVersionInfoSize = sizeof(info_os); if ( !GetVersionEx( &info_os ) ) return FALSE; // if the platform is NT, query the service control manager if ( info_os.dwPlatformId == VER_PLATFORM_WIN32_NT ) { BOOL fRunning = FALSE; // open the service manager SC_HANDLE sch = OpenSCManager(NULL, NULL, GENERIC_READ ); if ( sch == NULL ) return FALSE; // get the service SC_HANDLE schW3 = OpenService(sch, _T("W3SVC"), SERVICE_QUERY_STATUS ); if ( sch == NULL ) { CloseServiceHandle( sch ); return FALSE; } // query the service status SERVICE_STATUS status; ZeroMemory( &status, sizeof(status) ); if ( QueryServiceStatus(schW3, &status) ) { fRunning = (status.dwCurrentState == SERVICE_RUNNING); } CloseServiceHandle( schW3 ); CloseServiceHandle( sch ); // return the answer return fRunning; } // if the platform is Windows95, see if the object exists if ( info_os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) { HANDLE hEvent; BOOL fFound = FALSE; hEvent = CreateEvent(NULL, TRUE, FALSE, _T(PWS_SHUTDOWN_EVENT)); if ( hEvent != NULL ) { fFound = (GetLastError() == ERROR_ALREADY_EXISTS); CloseHandle(hEvent); } return(fFound); } return FALSE; } //--------------------------------------------------------------------------- // This routine is called on a timer event. The timer events only come if we // have received a shutdown notify callback from the metabase. So the server // is down. We need to wait around until it comes back up, then show ourselves. void CheckIfServerIsRunningAgain() { // see if the server is running. If it is, show the icon and stop the timer. if ( FIsW3Running() && g_hwnd ) { // if we can't use the metabase, there is no point in this if ( !g_pMBCom && !InitializeMetabase(NULL) ) { return; } // if we can't use the sink, there is no point in this if ( !g_pEventSink && !InitializeSink() ) { TerminateMetabase(); return; } // stop the life timer mechanism KillTimer( g_hwnd, PWS_TRAY_CHECKFORSERVERRESTART ); // start the unexpected server death test timer SetTimer( g_hwnd, PWS_TRAY_CHECKTOSEEIFINETINFODIED, TIMER_SERVERDIED, NULL ); // show the tray icon g_dwServerState = DWGetServerState(); FUpdateTrayIcon( NIM_ADD ); } } //--------------------------------------------------------------------------- // This routine is called on a timer event. The timer events only come if we know // the server is running. To avoid wasting too many extra cycles it is called rather // infrequently. What we are doing here is attempting to see if the server died // without sending us proper notification. If it did, we should clean up the // icon and start waiting for it to come back. void CheckIfServerUpAndDied() { if ( !FIsW3Running() ) { // hide the tray icon FUpdateTrayIcon( NIM_DELETE ); // disconnect the sink mechanism TerminateSink(); // disconnect from the metabase TerminateMetabase(); // stop the death timer KillTimer( g_hwnd, PWS_TRAY_CHECKTOSEEIFINETINFODIED ); // start the life timer SetTimer( g_hwnd, PWS_TRAY_CHECKFORSERVERRESTART, TIMER_RESTART, NULL ); } } //--------------------------------------------------------------------------- BOOL FUpdateTrayIcon( DWORD dwMessage ) { NOTIFYICONDATA dataIcon; // prepare the common parts of the icon data structure dataIcon.cbSize = sizeof( dataIcon ); dataIcon.hWnd = g_hwnd; dataIcon.uID = DW_TRAY_ICON_ID; dataIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; dataIcon.uCallbackMessage = WM_PWS_TRAY_SHELL_NOTIFY; // prepare the state-dependant icon handle switch( g_dwServerState ) { case MD_SERVER_STATE_PAUSED: case MD_SERVER_STATE_PAUSING: dataIcon.hIcon = LoadIcon( g_hInstance, MAKEINTRESOURCE(IDI_PAUSED) ); break; case MD_SERVER_STATE_STARTED: case MD_SERVER_STATE_STARTING: dataIcon.hIcon = LoadIcon( g_hInstance, MAKEINTRESOURCE(IDI_RUNNING) ); break; case MD_SERVER_STATE_STOPPED: case MD_SERVER_STATE_STOPPING: dataIcon.hIcon = LoadIcon( g_hInstance, MAKEINTRESOURCE(IDI_STOPPED) ); break; }; // prepare the state-dependant tip strings switch( g_dwServerState ) { case MD_SERVER_STATE_PAUSED: LoadString( g_hInstance, IDS_PAUSED, dataIcon.szTip, 63 ); break; case MD_SERVER_STATE_PAUSING: LoadString( g_hInstance, IDS_PAUSING, dataIcon.szTip, 63 ); break; case MD_SERVER_STATE_STARTED: LoadString( g_hInstance, IDS_STARTED, dataIcon.szTip, 63 ); break; case MD_SERVER_STATE_STARTING: LoadString( g_hInstance, IDS_STARTING, dataIcon.szTip, 63 ); break; case MD_SERVER_STATE_STOPPED: LoadString( g_hInstance, IDS_STOPPED, dataIcon.szTip, 63 ); break; case MD_SERVER_STATE_STOPPING: LoadString( g_hInstance, IDS_STOPPING, dataIcon.szTip, 63 ); break; }; DWORD err = GetLastError(); // make the shell call return Shell_NotifyIcon( dwMessage, &dataIcon ); } //--------------------------------------------------------------------------- DWORD DWGetServerState() { HRESULT hErr; METADATA_HANDLE hMeta; METADATA_RECORD mdRecord; DWORD state = 1000; DWORD dwRequiredLen; // open the w3svc key so we can get its state // since this is a pws thing, we only care about the first // server instance hErr = g_pMBCom->OpenKey( METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC/1/", METADATA_PERMISSION_READ, MB_TIMEOUT, &hMeta); if ( FAILED(hErr) ) return state; // prepare the metadata record mdRecord.dwMDIdentifier = MD_SERVER_STATE; mdRecord.dwMDAttributes = METADATA_INHERIT; mdRecord.dwMDUserType = IIS_MD_UT_SERVER; mdRecord.dwMDDataType = DWORD_METADATA; mdRecord.dwMDDataLen = sizeof(DWORD); mdRecord.pbMDData = (PBYTE)&state; // get the data hErr = g_pMBCom->GetData( hMeta, L"", &mdRecord, &dwRequiredLen ); // close the key hErr = g_pMBCom->CloseKey( hMeta ); // return the answer return state; } //--------------------------------------------------------------------------- // deal with mouse messages that occur on the tray icon void On_WM_PWS_TRAY_SHELL_NOTIFY(UINT uID, UINT uMouseMsg) { if ( uID != DW_TRAY_ICON_ID ) return; // act on the mouse message switch( uMouseMsg ) { case WM_LBUTTONDBLCLK: LaunchAdminUI(); break; case WM_RBUTTONDOWN: RunContextMenu(); break; } } //--------------------------------------------------------------------------- // the window proc callback procedure LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch( message ) { case WM_CREATE: // get the registered windows message signifying that there is a new // taskbar. When this message is triggered, we need to re-insert the icon // into the taskbar. This is usually done when the shell restarts. // see http://www.microsoft.com/msdn/sdk/inetsdk/help/itt/shell/taskbar.htm // also see the default case for processing of the message g_dwNewTaskbarMessage = RegisterWindowMessage(TEXT("TaskbarCreated")); break; case WM_PWS_TRAY_SHUTDOWN_NOTIFY: // hide the tray icon FUpdateTrayIcon( NIM_DELETE ); // disconnect the sink mechanism TerminateSink(); // disconnect from the metabase TerminateMetabase(); // start the time mechanism SetTimer( g_hwnd, PWS_TRAY_CHECKFORSERVERRESTART, TIMER_RESTART, NULL ); break; case WM_TIMER: switch ( wParam ) { case PWS_TRAY_CHECKFORSERVERRESTART: CheckIfServerIsRunningAgain(); break; case PWS_TRAY_CHECKTOSEEIFINETINFODIED: CheckIfServerUpAndDied(); break; }; break; case WM_PWS_TRAY_SHELL_NOTIFY: On_WM_PWS_TRAY_SHELL_NOTIFY( (UINT)wParam, (UINT)lParam ); break; case WM_PWS_TRAY_UPDATE_STATE: g_dwServerState = DWGetServerState(); FUpdateTrayIcon( NIM_MODIFY ); break; case WM_DESTROY: FUpdateTrayIcon( NIM_DELETE ); PostQuitMessage(0); break; case WM_COMMAND: switch( LOWORD(wParam) ) { case ID_START: SetServerState( MD_SERVER_COMMAND_START ); break; case ID_STOP: SetServerState( MD_SERVER_COMMAND_STOP ); break; case ID_PAUSE: SetServerState( MD_SERVER_COMMAND_PAUSE ); break; case ID_CONTINUE: SetServerState( MD_SERVER_COMMAND_CONTINUE ); break; case ID_PROPERTIES: LaunchAdminUI(); break; }; break; default: // cannot case directly on g_dwNewTaskbarMessage because it is not a constant if ( message == g_dwNewTaskbarMessage ) { // Just go straight into waitin' for the server mode. If the server is // running it will catch the first time throught the timer. If it is not, // then we just sit around and wait for it // start the time mechanism SetTimer( g_hwnd, PWS_TRAY_CHECKFORSERVERRESTART, TIMER_RESTART, NULL ); } break; } return(DefWindowProc(hWnd, message, wParam, lParam )); } //--------------------------------------------------------------------------- // main routine int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow ) { WNDCLASS wndclass; MSG msg; // record the instance handle g_hInstance = hInstance; // do nothing if this app is already running if ( hPrevInstance ) return 0; // one more test of previous instances if ( FindWindow(PWS_TRAY_WINDOW_CLASS,NULL) ) return 0; // start ole if ( FAILED(CoInitialize( NULL )) ) return 0; // get the menu handle g_hMenuMain = LoadMenu( g_hInstance, MAKEINTRESOURCE(IDR_POPUP) ); // prepare and register the window class wndclass.style = 0; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = NULL; wndclass.hCursor = NULL; wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = PWS_TRAY_WINDOW_CLASS; RegisterClass( &wndclass ); // create the window g_hwnd = CreateWindow( PWS_TRAY_WINDOW_CLASS, // pointer to registered class name _T(""), // pointer to window name 0, // window style 0, // horizontal position of window 0, // vertical position of window 0, // window width 0, // window height NULL, // handle to parent or owner window NULL, // handle to menu or child-window identifier hInstance, // handle to application instance NULL // pointer to window-creation data ); // Just go straight into waitin' for the server mode. If the server is // running it will catch the first time throught the timer. If it is not, // then we just sit around and wait for it // start the time mechanism SetTimer( g_hwnd, PWS_TRAY_CHECKFORSERVERRESTART, TIMER_RESTART, NULL ); // run the message loop while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage( &msg); } // clean up the sink and the metabase DestroyMenu( g_hMenuMain ); TerminateSink(); TerminateMetabase(); CoUninitialize(); return((int)msg.wParam); } //------------------------------------------------------------------ BOOL InitializeSink() { IConnectionPointContainer * pConnPointContainer = NULL; HRESULT hRes; BOOL fSinkConnected = FALSE; // g_pMBCom is defined in wrapmb IUnknown* pmb = (IUnknown*)g_pMBCom; g_pEventSink = new CImpIMSAdminBaseSink(); if ( !g_pEventSink ) { return FALSE; } // // First query the object for its Connection Point Container. This // essentially asks the object in the server if it is connectable. // hRes = pmb->QueryInterface( IID_IConnectionPointContainer, (PVOID *)&pConnPointContainer); if SUCCEEDED(hRes) { // Find the requested Connection Point. This AddRef's the // returned pointer. hRes = pConnPointContainer->FindConnectionPoint( IID_IMSAdminBaseSink, &g_pConnPoint); if (SUCCEEDED(hRes)) { hRes = g_pConnPoint->Advise( (IUnknown *)g_pEventSink, &g_dwSinkCookie); if (SUCCEEDED(hRes)) { fSinkConnected = TRUE; } } if ( pConnPointContainer ) { pConnPointContainer->Release(); pConnPointContainer = NULL; } } if ( !fSinkConnected ) { delete g_pEventSink; g_pEventSink = NULL; } return fSinkConnected; } //------------------------------------------------------------------ void TerminateSink() { HRESULT hRes; if ( g_dwSinkCookie && g_pConnPoint ) hRes = g_pConnPoint->Unadvise( g_dwSinkCookie ); } //------------------------------------------------------------------ BOOL InitializeMetabase( OLECHAR* pocMachineName ) { IClassFactory* pcsfFactory = NULL; COSERVERINFO csiMachineName; COSERVERINFO* pcsiParam = NULL; HRESULT hresError; //release previous interface if needed if( g_pMBCom != NULL ) { g_pMBCom->Release(); g_pMBCom = NULL; } //fill the structure for CoGetClassObject ZeroMemory( &csiMachineName, sizeof(csiMachineName) ); // csiMachineName.pAuthInfo = NULL; // csiMachineName.dwFlags = 0; // csiMachineName.pServerInfoExt = NULL; csiMachineName.pwszName = pocMachineName; pcsiParam = &csiMachineName; hresError = CoGetClassObject(GETAdminBaseCLSID(TRUE), CLSCTX_SERVER, pcsiParam, IID_IClassFactory, (void**) &pcsfFactory); if (FAILED(hresError)) return FALSE; // create the instance of the interface hresError = pcsfFactory->CreateInstance(NULL, IID_IMSAdminBase, (void **)&g_pMBCom); if (FAILED(hresError)) { g_pMBCom = FALSE; return FALSE; } // release the factory pcsfFactory->Release(); // success return TRUE; } //------------------------------------------------------------------ void TerminateMetabase() { if ( g_pMBCom ) { g_pMBCom->Release(); g_pMBCom = NULL; } } //======================================== control actions //------------------------------------------------------------------------ // get the inetinfo path BOOL LaunchAdminUI() { TCHAR chPath[MAX_PATH+1]; // get the path to the admin UI if ( !GetAdminPath( chPath, MAX_PATH ) ) { LoadString( g_hInstance, IDS_ADMINUI_ERR, chPath, MAX_PATH ); MessageBox( g_hwnd, chPath, NULL, MB_OK|MB_ICONERROR ); return FALSE; } // do it ShellExecute( NULL, // handle to parent window NULL, // pointer to string that specifies operation to perform chPath, // pointer to filename or folder name string NULL, // pointer to string that specifies executable-file parameters NULL, // pointer to string that specifies default directory SW_SHOW // whether file is shown when opened ); return TRUE; } //------------------------------------------------------------------------ // get the inetinfo path BOOL GetAdminPath( TCHAR* pch, WORD cch ) { HKEY hKey; DWORD err, type; DWORD cbBuff = cch * sizeof(TCHAR); // get the server install path from the registry // open the registry key, if it exists err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, // handle of open key REGKEY_STP, // address of name of subkey to open 0, // reserved KEY_READ, // security access mask &hKey // address of handle of open key ); // if we did not open the key for any reason (say... it doesn't exist) // then leave right away if ( err != ERROR_SUCCESS ) return FALSE; type = REG_SZ; err = RegQueryValueEx( hKey, // handle of key to query REGKEY_INSTALLKEY, // address of name of value to query NULL, // reserved &type, // address of buffer for value type (PUCHAR)pch, // address of data buffer &cbBuff // address of data buffer size ); // close the key RegCloseKey( hKey ); // if we did get the key for any reason (say... it doesn't exist) // then leave right away if ( err != ERROR_SUCCESS ) return FALSE; // tack on the file name itself TCHAR chFile[64]; if ( LoadString( g_hInstance, IDS_ADMIN_UI, chFile, 63 ) == 0 ) return FALSE; _tcscat( pch, chFile ); // success return TRUE; } //------------------------------------------------------------------------ BOOL SetServerState( DWORD dwControlCode ) { HRESULT hErr; METADATA_HANDLE hMeta; METADATA_RECORD mdRecord; // open the w3svc key so we can get its state // since this is a pws thing, we only care about the first // server instance hErr = g_pMBCom->OpenKey( METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC/1/", METADATA_PERMISSION_WRITE, MB_TIMEOUT, &hMeta); if ( FAILED(hErr) ) return FALSE; // prepare the metadata record mdRecord.dwMDIdentifier = MD_SERVER_COMMAND; mdRecord.dwMDAttributes = 0; mdRecord.dwMDUserType = IIS_MD_UT_SERVER; mdRecord.dwMDDataType = DWORD_METADATA; mdRecord.dwMDDataLen = sizeof(DWORD); mdRecord.pbMDData = (PBYTE)&dwControlCode; // get the data hErr = g_pMBCom->SetData( hMeta, L"", &mdRecord ); // close the key hErr = g_pMBCom->CloseKey( hMeta ); // return the answer return TRUE; } //--------------------------------------------------------------------------- void RunContextMenu() { POINT pos; HMENU hMenuSub; RECT rect = {0,0,1,1}; BOOL f; static BOOL fTracking = FALSE; if ( fTracking ) return; fTracking = TRUE; // where is the mouse? This tells us where to put the menu if ( !g_hMenuMain || !GetCursorPos(&pos) ) return; // get the menu handle hMenuSub = GetSubMenu( g_hMenuMain, 0 ); // easiest to start by disabling all the state based items f = EnableMenuItem( hMenuSub, ID_START, MF_BYCOMMAND|MF_GRAYED ); f = EnableMenuItem( hMenuSub, ID_STOP, MF_BYCOMMAND|MF_GRAYED ); f = EnableMenuItem( hMenuSub, ID_PAUSE, MF_BYCOMMAND|MF_GRAYED ); f = EnableMenuItem( hMenuSub, ID_CONTINUE, MF_BYCOMMAND|MF_GRAYED ); // prepare the state based menu items switch( g_dwServerState ) { case MD_SERVER_STATE_PAUSED: case MD_SERVER_STATE_PAUSING: EnableMenuItem( hMenuSub, ID_STOP, MF_BYCOMMAND|MF_ENABLED ); EnableMenuItem( hMenuSub, ID_CONTINUE, MF_BYCOMMAND|MF_ENABLED ); break; case MD_SERVER_STATE_STARTED: case MD_SERVER_STATE_STARTING: EnableMenuItem( hMenuSub, ID_STOP, MF_BYCOMMAND|MF_ENABLED ); EnableMenuItem( hMenuSub, ID_PAUSE, MF_BYCOMMAND|MF_ENABLED ); break; case MD_SERVER_STATE_STOPPED: case MD_SERVER_STATE_STOPPING: EnableMenuItem( hMenuSub, ID_START, MF_BYCOMMAND|MF_ENABLED ); break; }; // this is necessary, because we need to get a lose focus message for the menu // to go down when the user click on some other process outside the up menu SetForegroundWindow(g_hwnd); // run the menu TrackPopupMenu(hMenuSub, TPM_LEFTALIGN|TPM_RIGHTBUTTON, pos.x, pos.y, 0, g_hwnd, NULL); fTracking = FALSE; }