/*++ Copyright (c) 1997 Microsoft Corporation Module Name: idevices.cpp Abstract: Internal implementation for the devices subfolder. Environment: WIN32 User Mode Author: Darwin Ouyang (t-darouy) 30-Sept-1997 --*/ #include "StdAfx.h" #include "inode.h" // base class #include "iroot.h" // iroot #include "idevices.h" // devices folder #include "idevice.h" // a device #include "faxsnapin.h" // snapin #include "faxdataobj.h" // dataobject #include "faxstrt.h" // string table #pragma hdrstop extern CStringTable * GlobalStringTable; // Generated with uuidgen. Each node must have a GUID associated with it. // This one is for the devices subfolder. const GUID GUID_DevicesNode = /* 03a815d8-3e9e-11d1-9075-00a0c90ab504 */ { 0x03a815d8, 0x3e9e, 0x11d1, {0x90, 0x75, 0x00, 0xa0, 0xc9, 0x0a, 0xb5, 0x04} }; //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// // // // Constructor and destructor // // CInternalDevices::CInternalDevices( CInternalNode * pParent, CFaxComponentData * pCompData ) : CInternalNode( pParent, pCompData ), pDevicesInfo( NULL ) /*++ Routine Description: Constructor Arguments: pParent - pointer to parent node, in this case unused pCompData - pointer to IComponentData implementation for snapin global data Return Value: None. --*/ { DebugPrint(( TEXT("CInternalDeviceS Created") )); faxHandle = m_pCompData->m_FaxHandle; assert( faxHandle != NULL ); } CInternalDevices::~CInternalDevices( ) /*++ Routine Description: Destructor Arguments: None. Return Value: None. --*/ { DebugPrint(( TEXT("CInternalDeviceS Destroyed") )); } //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// // // // Mandatory CInternalNode implementations. // // const GUID * CInternalDevices::GetNodeGUID() /*++ Routine Description: Returns the node's associated GUID. Arguments: None. Return Value: A const pointer to a binary GUID. --*/ { // DebugPrint(( TEXT("Trace: CInternalDevices::GetNodeGUID") )); return &GUID_DevicesNode; } const LPTSTR CInternalDevices::GetNodeDisplayName() /*++ Routine Description: Returns a const TSTR pointer to the node's display name. Arguments: None. Return Value: A const pointer to a TSTR. --*/ { // DebugPrint(( TEXT("Trace: CInternalDevices::GetNodeDisplayName") )); return ::GlobalStringTable->GetString( IDS_DEVICESNODENAME ); } const LPTSTR CInternalDevices::GetNodeDescription() /*++ Routine Description: Returns a const TSTR pointer to the node's display description. Arguments: None. Return Value: A const pointer to a TSTR. --*/ { // DebugPrint(( TEXT("Trace: CInternalDevices::GetNodeDisplayName") )); return ::GlobalStringTable->GetString( IDS_DEVICES_FOLDER_DESC_ROOT ); } const LONG_PTR CInternalDevices::GetCookie() /*++ Routine Description: Returns the cookie for this node. Arguments: None. Return Value: A const long containing the cookie for the pointer, in this case, (long)this. --*/ { // DebugPrint(( TEXT("Trace: CInternalDevices::GetCookie") )); DebugPrint(( TEXT("Devices Node Cookie: 0x%p"), this )); return (LONG_PTR)this; // status node's cookie is the node id. } //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// // // // Internal Event Handlers // // HRESULT CInternalDevices::ResultOnShow( IN CFaxComponent* pComp, IN CFaxDataObject * lpDataObject, IN LPARAM arg, IN LPARAM param) /*++ Routine Description: Event handler for the MMCN_SHOW message for the devices node. Arguments: pComp - a pointer to the instance of IComponentData which this root node is associated with. lpDataObject - a pointer to the data object containing context information for this node. arg, param - the arguements of the message Return Value: HRESULT which indicates SUCCEEDED() or FAILED() --*/ { DebugPrint(( TEXT("Trace: CInternalDevices::ResultOnShow") )); HRESULT hr = S_OK; unsigned int count; int iResult; LPHEADERCTRL pIHeaderCtrl; if( m_pCompData->QueryRpcError() ) { return E_UNEXPECTED; } if( arg == TRUE ) { // need to display result pane do { // get resultdata pointer pIResultData = pComp->m_pResultData; assert( pIResultData ); if( pIResultData == NULL ) { hr = E_UNEXPECTED; break; } // insert the icons into the image list hr = pComp->InsertIconsIntoImageList(); assert( SUCCEEDED( hr ) ); if( FAILED( hr ) ) { break; } // set headers pIHeaderCtrl = pComp->m_pHeaderCtrl; hr = pIHeaderCtrl->InsertColumn( 0, ::GlobalStringTable->GetString( IDS_DEVICE_NAME ), LVCFMT_LEFT, MMCLV_AUTO ); if( FAILED( hr ) ) { break; } hr = pIHeaderCtrl->InsertColumn( 1, ::GlobalStringTable->GetString( IDS_DEVICE_SEND_EN ), LVCFMT_LEFT, MMCLV_AUTO ); if( FAILED( hr ) ) { break; } hr = pIHeaderCtrl->InsertColumn( 2, ::GlobalStringTable->GetString( IDS_DEVICE_RECV_EN ), LVCFMT_LEFT, MMCLV_AUTO ); if( FAILED( hr ) ) { break; } hr = pIHeaderCtrl->InsertColumn( 3, ::GlobalStringTable->GetString( IDS_DEVICE_TSID ), LVCFMT_LEFT, MMCLV_AUTO ); if( FAILED( hr ) ) { break; } hr = pIHeaderCtrl->InsertColumn( 4, ::GlobalStringTable->GetString( IDS_DEVICE_CSID ), LVCFMT_LEFT, MMCLV_AUTO ); if( FAILED( hr ) ) { break; } hr = pIHeaderCtrl->InsertColumn( 5, ::GlobalStringTable->GetString( IDS_DEVICE_STATUS ), LVCFMT_LEFT, MMCLV_AUTO ); if( FAILED( hr ) ) { break; } hr = pIHeaderCtrl->InsertColumn( 6, ::GlobalStringTable->GetString( IDS_DEVICE_PRI ), LVCFMT_LEFT, MMCLV_AUTO ); if( FAILED( hr ) ) { break; } // this is the first time initializing the devices node if( pComp->pDeviceArray == NULL ) { // get fax info try { if( !FaxEnumPorts( faxHandle, &pDevicesInfo, &pComp->numDevices ) ) { if (GetLastError() == ERROR_ACCESS_DENIED) { ::GlobalStringTable->SystemErrorMsg(ERROR_ACCESS_DENIED); } else { m_pCompData->NotifyRpcError( TRUE ); hr = m_pCompData->m_pConsole->MessageBox(::GlobalStringTable->GetString( IDS_FAX_RETR_DEV_FAIL ), ::GlobalStringTable->GetString( IDS_ERR_TITLE ), MB_OK, &iResult); } hr = E_UNEXPECTED; break; } } catch( ... ) { m_pCompData->NotifyRpcError( TRUE ); hr = m_pCompData->m_pConsole->MessageBox(::GlobalStringTable->GetString( IDS_FAX_RETR_DEV_FAIL ), ::GlobalStringTable->GetString( IDS_ERR_TITLE ), MB_OK, &iResult); hr = E_UNEXPECTED; break; } assert( pComp->pDeviceArray == NULL ); pComp->pDeviceArray = new pCInternalDevice[pComp->numDevices]; if (!pComp->pDeviceArray) { hr = E_OUTOFMEMORY; break; } ZeroMemory( (void *)pComp->pDeviceArray, pComp->numDevices * sizeof( pCInternalDevice ) ); for( count = 0; count < pComp->numDevices; count ++ ) { pComp->pDeviceArray[count] = new CInternalDevice( this, m_pCompData, faxHandle, pDevicesInfo[count].DeviceId ); if (!pComp->pDeviceArray[count]) { hr = E_OUTOFMEMORY; break; } } } // on each display, insert each device into the devices pane for( count = 0; count < pComp->numDevices; count++ ) { hr = InsertItem( &pComp->pDeviceArray[count], &(pDevicesInfo[count]) ); if( FAILED( hr ) ) { break; } } } while( 0 ); if( pDevicesInfo != NULL ) { FaxFreeBuffer( (PVOID) pDevicesInfo ); pDevicesInfo = NULL; } if (FAILED(hr)) { if (pComp->pDeviceArray) { for (count = 0; count < pComp->numDevices; count++ ) { if (pComp->pDeviceArray[count]) { delete(pComp->pDeviceArray[count]); } } delete(pComp->pDeviceArray); pComp->pDeviceArray = NULL; } } } return hr; } HRESULT CInternalDevices::ResultOnDelete( IN CFaxComponent* pComp, IN CFaxDataObject * lpDataObject, IN LPARAM arg, IN LPARAM param) /*++ Routine Description: Event handler for the MMCN_DELETE message for the devices node. Arguments: pComp - a pointer to the instance of IComponentData which this root node is associated with. lpDataObject - a pointer to the data object containing context information for this node. pdo - a pointer to the data object associated with this node arg, param - the arguements of the message Return Value: HRESULT which indicates SUCCEEDED() or FAILED() --*/ { // unneeded because the per result view data will be deleted by destroying the // CInternalComponent holding the information #if 0 DebugPrint(( TEXT("Trace: CInternalDevices::ResultOnDelete") )); unsigned int count; for( count = 0; count < pComp->numDevices; count++ ) { if( pComp->pDeviceArray[count] != NULL ) { delete pComp->pDeviceArray[count]; pComp->pDeviceArray[count] = NULL; } } delete pComp->pDeviceArray; pComp->pDeviceArray = NULL; #endif return S_OK; } //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// // // // Internal Functions // // HRESULT CInternalDevices::CorrectServiceState() /*++ Routine Description: Scans the devices to see if any are enabled for receive, and if so, resets the service's startup state to automatic from manual. If no devices are enabled for receive, then the service is set from automatic to manual. Arguments: None. Return Value: HRESULT which indicates SUCCEEDED() or FAILED() --*/ { DWORD i; PFAX_PORT_INFO pPortInfos = NULL; DWORD portCount; int iResult; BOOL setAuto = FALSE; HRESULT hr = S_OK; DebugPrint(( TEXT("Trace: CInternalDevices::CorrectServiceState") )); do { // get fax info try { if( m_pCompData->QueryRpcError() ) { hr = E_UNEXPECTED; break; } if( !FaxEnumPorts( faxHandle, &pPortInfos, &portCount ) ) { if (GetLastError() == ERROR_ACCESS_DENIED) { ::GlobalStringTable->SystemErrorMsg(ERROR_ACCESS_DENIED); } else { m_pCompData->NotifyRpcError( TRUE ); hr = m_pCompData->m_pConsole->MessageBox(::GlobalStringTable->GetString( IDS_FAX_RETR_DEV_FAIL ), ::GlobalStringTable->GetString( IDS_ERR_TITLE ), MB_OK, &iResult); } hr = E_UNEXPECTED; break; } } catch( ... ) { m_pCompData->NotifyRpcError( TRUE ); hr = m_pCompData->m_pConsole->MessageBox(::GlobalStringTable->GetString( IDS_FAX_RETR_DEV_FAIL ), ::GlobalStringTable->GetString( IDS_ERR_TITLE ), MB_OK, &iResult); hr = E_UNEXPECTED; break; } for( i = 0; i < portCount; i++) { if( pPortInfos[i].Flags & FPF_RECEIVE ) { setAuto = TRUE; break; } } SetServiceState( setAuto ); } while( 0 ); if( pPortInfos != NULL ) { FaxFreeBuffer( (PVOID) pPortInfos ); pPortInfos = NULL; } return hr; } HRESULT CInternalDevices::SetServiceState( IN BOOL fAutomatic ) /*++ Routine Description: Opens the Service Manager on the snapin's target machine, and sets the fax service state according to the fAutomatic parameter. Arguments: fAutomatic - if TRUE, sets the service to start automatically. if FALSE, sets the service to start manually. Return Value: HRESULT which indicates SUCCEEDED() or FAILED() --*/ { SC_LOCK sclLock; LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf; TCHAR buffer[256]; LPTSTR str; int iResult; HRESULT hr = S_OK; SC_HANDLE schSCManager; SC_HANDLE schService; DebugPrint(( TEXT("Trace: CInternalDevices::SetServiceState") )); DWORD dwBytesNeeded, dwStartType; ZeroMemory( (PVOID)buffer, sizeof( TCHAR ) * 256 ); schSCManager = OpenSCManager( ((CInternalRoot*)m_pParentINode)->GetMachine(), NULL, SC_MANAGER_CONNECT | SC_MANAGER_LOCK | SC_MANAGER_QUERY_LOCK_STATUS | SC_MANAGER_ENUMERATE_SERVICE ); if( schSCManager == NULL ) { ::GlobalStringTable->PopUpMsg( NULL, IDS_ERR_CONNECT_SCM, TRUE, 0 ); assert( FALSE ); return E_UNEXPECTED; } // Need to acquire database lock before reconfiguring. sclLock = LockServiceDatabase(schSCManager); // If the database cannot be locked, report the details. if(sclLock == NULL) { // Exit if the database is not locked by another process. if(GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) { ::GlobalStringTable->PopUpMsg( NULL, IDS_ERR_LOCK_SERVICE_DB, TRUE, 0 ); assert( FALSE ); return E_UNEXPECTED; } // Allocate a buffer to get details about the lock. lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS) LocalAlloc( LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256); if(lpqslsBuf == NULL) { ::GlobalStringTable->PopUpMsg( NULL, IDS_OUT_OF_MEMORY, TRUE, 0 ); assert( FALSE ); return E_OUTOFMEMORY; } do { // Get and print the lock status information. if(!QueryServiceLockStatus( schSCManager, lpqslsBuf, sizeof(QUERY_SERVICE_LOCK_STATUS)+256, &dwBytesNeeded) ) { ::GlobalStringTable->PopUpMsg( NULL, IDS_ERR_QUERY_LOCK, TRUE, 0 ); assert( FALSE ); break; } if(lpqslsBuf->fIsLocked) { str = ::GlobalStringTable->GetString( IDS_QUERY_LOCK ); _stprintf( buffer, str, lpqslsBuf->lpLockOwner, lpqslsBuf->dwLockDuration ); m_pCompData->m_pConsole->MessageBox( buffer, ::GlobalStringTable->GetString( IDS_ERR_TITLE ), MB_OK, &iResult); } else { ::GlobalStringTable->PopUpMsg( NULL, IDS_ERR_LOCK_SERVICE_DB, TRUE, 0 ); } } while( 0 ); LocalFree(lpqslsBuf); return E_UNEXPECTED; } do { // The database is locked, so it is safe to make changes. // Open a handle to the service. schService = OpenService( schSCManager, // SCManager database TEXT("Fax"), // name of service SERVICE_CHANGE_CONFIG ); // need CHANGE access if(schService == NULL) { ::GlobalStringTable->PopUpMsg( NULL, IDS_ERR_OPEN_SERVICE, TRUE, 0 ); assert( FALSE ); hr = E_UNEXPECTED; break; } dwStartType = (fAutomatic) ? SERVICE_AUTO_START : SERVICE_DEMAND_START; if(! ChangeServiceConfig( schService, // handle of service SERVICE_NO_CHANGE, // service type: no change dwStartType, // change service start type SERVICE_NO_CHANGE, // error control: no change NULL, // binary path: no change NULL, // load order group: no change NULL, // tag ID: no change NULL, // dependencies: no change NULL, // account name: no change NULL, // password: no change NULL) ) { // display string ::GlobalStringTable->PopUpMsg( NULL, IDS_ERR_CHANGE_SERVICE, TRUE, 0 ); assert( FALSE ); hr = E_UNEXPECTED; } // Close the handle to the service. CloseServiceHandle(schService); } while( 0 ); // Release the database lock. UnlockServiceDatabase(sclLock); return hr; } void CInternalDevices::NotifyFailure( CFaxComponent * pComp ) /*++ Routine Description: If there was a failure in a RPC call anywhere in the child nodes, the result pane should be cleared. This function clears the result pane. Arguments: pComp - a pointer to the instance of IComponentData which this root node is associated with. Return Value: None. --*/ { assert( pComp != NULL ); assert( pComp->pDeviceArray != NULL ); pComp->m_pResultData->DeleteAllRsltItems(); } //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// // // // Utility Functions // // HRESULT CInternalDevices::InsertItem( IN CInternalDevice ** pDevice, IN PFAX_PORT_INFO pDeviceInfo ) /*++ Routine Description: Wrapper that inserts an item into the result view pane given some information. Arguments: pDevice - the instance of CInternalDevice to insert pDeviceInfo - the information associated with that log category. Return Value: HRESULT which indicates SUCCEEDED() or FAILED() --*/ { RESULTDATAITEM rdi; HRESULT hr = S_OK; ZeroMemory( &rdi, sizeof( RESULTDATAITEM ) ); rdi.mask = RDI_STR | RDI_IMAGE | RDI_PARAM; rdi.nCol = 0; rdi.bScopeItem = FALSE; rdi.lParam = (*pDevice)->GetCookie(); rdi.nImage = (*pDevice)->GetNodeDisplayImage(); rdi.str = MMC_CALLBACK; hr = pIResultData->InsertItem( &rdi ); assert( SUCCEEDED( hr ) ); (*pDevice)->SetItemID( rdi.itemID ); return hr; }