Beispiel #1
0
CNdasEventMonitor::~CNdasEventMonitor()
{
	if (INVALID_HANDLE_VALUE != m_hHeartbeatMonitorTimer) {
		BOOL fSuccess = ::CloseHandle(m_hHeartbeatMonitorTimer);
		if (!fSuccess) {
			DPWarningEx(_FT("Failed to close Heartbeat Monitor Timer Handle: "));
		}
	}

	if (NULL != m_hLogDeviceSetChangeEvent) {
		BOOL fSuccess = ::CloseHandle(m_hLogDeviceSetChangeEvent);
		if (!fSuccess) {
			DPWarningEx(_FT("Failed to close Logical device set change event: "));
		}
	}
}
Beispiel #2
0
BOOL 
CNdasDeviceRegistrar::Unregister(const NDAS_DEVICE_ID& DeviceId)
{
	ximeta::CAutoLock autolock(this);

	CNdasDeviceId cdevid(DeviceId);
	DPInfo(_FT("Unregister device %s\n"), (LPCTSTR)cdevid);

	DeviceIdMap::iterator itrId = m_deviceIdMap.find(DeviceId);
	if (m_deviceIdMap.end() == itrId) {
		// TODO: ::SetLastError(NDAS_ERROR_DEVICE_NOT_FOUND);
		// TODO: Make more specific error code
		::SetLastError(NDASHLPSVC_ERROR_DEVICE_ENTRY_NOT_FOUND);
	}

	PCNdasDevice pDevice = itrId->second;
	
	if (pDevice->GetStatus() != NDAS_DEVICE_STATUS_DISABLED) {
		// TODO: ::SetLastError(NDAS_ERROR_CANNOT_UNREGISTER_ENABLED_DEVICE);
		// TODO: Make more specific error code
		::SetLastError(NDASHLPSVC_ERROR_CANNOT_UNREGISTER_ENABLED_DEVICE);
		return FALSE;
	}

	DWORD dwSlotNo = pDevice->GetSlotNo();
	
	_ASSERT(0 != dwSlotNo);

	DeviceSlotMap::iterator itrSlot = m_deviceSlotMap.find(dwSlotNo);

	m_deviceIdMap.erase(itrId);

	m_deviceSlotMap.erase(itrSlot);
	m_pbSlotOccupied[dwSlotNo] = FALSE;

	pDevice->Release();

	TCHAR szContainer[30];
	HRESULT hr = ::StringCchPrintf(szContainer, 30, TEXT("Devices\\%04d"), dwSlotNo);
	_ASSERT(SUCCEEDED(hr));

	BOOL fSuccess = _NdasSystemCfg.DeleteContainer(szContainer, TRUE);
	
	if (!fSuccess) {
		DPWarningEx(
			_FT("Deleting registration entry from the registry failed at %s.\n"), 
			szContainer);
	}

	CNdasInstanceManager* pInstMan = CNdasInstanceManager::Instance();
	_ASSERTE(NULL != pInstMan);

	CNdasEventPublisher* pEventPublisher = pInstMan->GetEventPublisher();
	_ASSERTE(NULL != pEventPublisher);

	(void) pEventPublisher->DeviceEntryChanged();

	return TRUE;
}
Beispiel #3
0
BOOL GetLocalLpxAddressList(
	IN DWORD cbBuffer,
	OUT LPSOCKET_ADDRESS_LIST lpBuffer,
	OUT LPDWORD pcbBytesReturned)
{
	SOCKET sock = ::WSASocket(
		AF_LPX, 
		SOCK_STREAM, 
		IPPROTO_LPXTCP,
		NULL,
		0,
		0);

	if (INVALID_SOCKET == sock) {
		DPErrorEx(_FT("Socket creation failed: "));
		return FALSE;
	}

	BOOL fSuccess = GetLocalLpxAddressList(
		sock, 
		cbBuffer,
		lpBuffer, 
		pcbBytesReturned);

	//
	// Close socket may shadow last error
	//
	int iWSALastError = ::WSAGetLastError();

	INT iResult = ::closesocket(sock);
	if (0 != iResult) {
		DPWarningEx(_FT("Closing a socket failed: "));
	}

	::WSASetLastError(iWSALastError);

	return fSuccess;
}
Beispiel #4
0
DWORD
CNdasEventPublisher::OnTaskStart()
{
	_ASSERTE(NULL != m_hSemQueue && "Don't forget to call initialize().");

	// Queue Semaphore, Terminating Thread, Pipe Instances(MAX...)
	HANDLE hWaitHandles[2 + MAX_NDAS_EVENT_PIPE_INSTANCES];
	hWaitHandles[0] = m_hTaskTerminateEvent;
	hWaitHandles[1] = m_hSemQueue;

	//
	// initial pipe instance
	//
	m_PipeData.clear();
	BOOL fSuccess = AcceptNewConnection();
	if (!fSuccess) {
		DPErrorEx(_T("Creating a first pipe instance failed: "));
		return -1;
	}

	BOOL bTerminate(FALSE);

	while (FALSE == bTerminate) {

		DWORD dwWaitHandles = 2 + m_PipeData.size();
		for (DWORD i = 0; i < m_PipeData.size(); ++i) {
			hWaitHandles[i + 2] = m_PipeData[i]->overlapped.hEvent;
		}

		DWORD dwWaitResult = ::WaitForMultipleObjects(
			dwWaitHandles,
			hWaitHandles,
			FALSE,
			m_dwPeriod);


		if (dwWaitResult == WAIT_OBJECT_0) 
		{
			//
			// Terminate Thread
			//
			bTerminate = TRUE;
		} 
		else if (dwWaitResult == WAIT_OBJECT_0 + 1) 
		{
			//
			// Event Message is queued
			//
			while (TRUE) {
				m_queueLock.Lock();
				bool bEmpty = m_EventMessageQueue.empty();
				if (bEmpty) {
					m_queueLock.Unlock();
					break;
				}
				NDAS_EVENT_MESSAGE message = m_EventMessageQueue.front();
				m_EventMessageQueue.pop();
				m_queueLock.Unlock();
				Publish(&message);
			}

		} 
		else if (dwWaitResult >= WAIT_OBJECT_0 + 2 && 
			dwWaitResult < WAIT_OBJECT_0 + 2 + m_PipeData.size())
		{
			DWORD dwPipe = dwWaitResult - WAIT_OBJECT_0 - 2;

			DPInfo(_FT("Event Client %d\n"), dwPipe);

			CLIENT_DATA* pCurClientData = m_PipeData[dwPipe];

			DPInfo(_FT("Connected: %d\n"), pCurClientData->bConnected);

			fSuccess = ::ResetEvent(pCurClientData->overlapped.hEvent);
			_ASSERT(fSuccess);

			if (!pCurClientData->bConnected) {
				
				//
				// create another instance
				//
				fSuccess = AcceptNewConnection();
				if (!fSuccess) {
					DPWarningEx(_FT("Creating another pipe instance failed: "));
					DPWarning(_FT("No more event subscribers can be accepted.\n"));
				}

				// AcceptNewConnection will invalidate pCurClientData;

				pCurClientData = m_PipeData.at(dwPipe);

				//
				// Accepting connection
				//
				pCurClientData->bConnected = TRUE;
				fSuccess = ::ResetEvent(pCurClientData->overlapped.hEvent);
				_ASSERT(fSuccess);
				
				//
				// Send a version event for connected client
				//
				fSuccess = SendVersionInfo(
					pCurClientData->hPipe, 
					&pCurClientData->overlapped);

				//
				// Any failure will disconnect the client
				//
				if (!fSuccess && ERROR_IO_PENDING != ::GetLastError()) {
					ClientDataVector::iterator itr = m_PipeData.begin();
					CleanupConnection(pCurClientData);
					while (itr != m_PipeData.end()) {
						if ((CLIENT_DATA*)*itr == pCurClientData) {
							m_PipeData.erase(itr);
							break;
						}
						++itr;
					}
					DPInfo(_FT("Accepted removed event subscriber.\n"));
				} else {
					DPInfo(_FT("Accepted new event subscriber.\n"));
				}

			} else {
			}
			// ignore other status
		} else if (WAIT_TIMEOUT == dwWaitResult) {
			NDAS_EVENT_MESSAGE msg = {0};
			msg.EventType = NDAS_EVENT_TYPE_PERIODIC;
			Publish(&msg);
		} else 
		{
			//
			// Error
			//
		}

	}

	//
	// TODO: Add cleanup
	//
	DWORD nPipeData = m_PipeData.size();
	ClientDataVector::iterator itr = m_PipeData.begin();
	while (itr != m_PipeData.end()) {
		CleanupConnection(*itr);
		++itr;
	}
	m_PipeData.clear();

	_tprintf(TEXT("Terminating Publisher Thread...\n"));
	return 0;
}
Beispiel #5
0
void 
CNdasEventPublisher::Publish(const PNDAS_EVENT_MESSAGE pMessage)
{
	DPInfo(_FT("Publishing Event: %s\n"), 
		NdasEventTypeString(pMessage->EventType));

	//
	// sent the message to the connected pipes
	//
	for (ClientDataVector::iterator itr = m_PipeData.begin(); 
		itr != m_PipeData.end();) 
		//
		// do not forward the iterator here when erasing some 
		// elements 
		// itr2 = v.erase(itr); 
		// itr2 has a forwarded iterator from itr
		//
	{
		CLIENT_DATA* pClientData = *itr;
		if (pClientData->bConnected) {

			DWORD cbWritten;

			BOOL fSuccess = ::WriteFile(
				pClientData->hPipe,
				pMessage,
				sizeof(NDAS_EVENT_MESSAGE),
				&cbWritten,
				&pClientData->overlapped);

			if (!fSuccess && ERROR_IO_PENDING != ::GetLastError()) {
				
				DPErrorEx(_FT("Writing to a pipe failed: "));
				DPInfo(_FT("Detaching an event subscriber.\n"));
				
				CleanupConnection(pClientData);
				//
				// erasing an element will automatically
				// forward the vector 
				// (actually, itr remains the same, but the itr is
				// a next elemen)
				itr = m_PipeData.erase(itr);

				//
				// create another instance
				//
				fSuccess = AcceptNewConnection();
				if (!fSuccess) {
					DPWarningEx(_FT("Creating another pipe instance failed: "));
					DPWarning(_FT("No more event subscribers can be accepted.\n"));
				}

			} else {
				//
				// forward the iterator if we did erase
				//
				DPInfo(_FT("Event written to a pipe %p.\n"), (*itr)->hPipe);
				++itr;
			}
		} else {
			//
			// forward the iterator if not connected
			//
			++itr;
		}
	}	
}
Beispiel #6
0
//////////////////////////////////////////////////////////////////////////
//
// NdasDiInstallService
//
//////////////////////////////////////////////////////////////////////////
//
// start type   SERVICE_DEMAND_START, SERVICE_BOOT_START,
//
NDASDI_API 
BOOL 
WINAPI
NdasDiInstallServiceSCH(
	SC_HANDLE schSCManager,
	LPCTSTR ServiceName,
	LPCTSTR DisplayName,
	LPCTSTR Description,
	DWORD DesiredAccess,
	DWORD ServiceType,
	DWORD StartType,
	DWORD ErrorControl,
	LPCTSTR BinaryPathName,
	LPCTSTR LoadOrderGroup,
	LPDWORD lpdwTagId,
	LPCTSTR Dependencies,
	LPCTSTR AccountName,
	LPCTSTR Password)
{

	DPInfo(_FT("Creating a service %s(DisplayName: %s, Descr: %s) ")
		_T("Access %d, %s, %s, %s, ")
		_T("Path %s, LoadOrderGroup %s, Dep %s, Account %s, Pwd %s\n"),
	   ServiceName,
	   (NULL == DisplayName) ? ServiceName : DisplayName,
	   Description,
	   DesiredAccess, 
	   pServiceTypeString(ServiceType), 
	   pServiceStartTypeString(StartType), 
	   pServiceErrorControlString(ErrorControl),
	   BinaryPathName, 
	   (LoadOrderGroup == NULL) ? _T("(none)") : LoadOrderGroup,
	   (Dependencies == NULL) ? _T("(none)") : Dependencies, 
	   (AccountName == NULL) ? _T("LocalSystem") : AccountName,
	   (Password == NULL) ? _T("(none)") : _T("(set)"));
	
	//
	// NOTE: This creates an entry for a standalone driver. If this
	//       is modified for use with a driver that requires a Tag,
	//       Group, and/or Dependencies, it may be necessary to
	//       query the registry for existing driver information
	//       (in order to determine a unique Tag, etc.).
	//

	AutoSCHandle schService = CreateService( 
		schSCManager,  
		ServiceName,   
		(NULL == DisplayName) ? ServiceName : DisplayName,
		DesiredAccess, 
		ServiceType, 
		StartType,	
		ErrorControl,
		BinaryPathName,
		LoadOrderGroup,
		lpdwTagId,   
		Dependencies,
		AccountName,
		Password);

	if (NULL == (SC_HANDLE) schService) {
		DPErrorEx(_FT("Creating a service %s failed: "), ServiceName);
		return FALSE;
	}

	if (NULL != Description) {
		SERVICE_DESCRIPTION svcDesc;
		svcDesc.lpDescription = (LPTSTR) Description;
		BOOL fSuccess = ChangeServiceConfig2(
			schService,
			SERVICE_CONFIG_DESCRIPTION,
			&svcDesc);
		if (!fSuccess) {
			DPWarningEx(_FT("Setting a service description failed: "));
		}
	}

	DPInfo(_FT("Service %s created successfully.\n"), ServiceName);
	return TRUE;
}
Beispiel #7
0
BOOL
CNdasIXBcast::BroadcastStatus()
{
	BUSENUM_QUERY_INFORMATION BusEnumQuery;
	BUSENUM_INFORMATION BusEnumInformation;

	CNdasLogicalDeviceManager* pLdm = pGetNdasLogicalDeviceManager();
	CNdasLogicalDeviceCollection coll;
	pLdm->Lock();
	pLdm->GetItems(coll);
	pLdm->Unlock();

	for (CNdasLogicalDeviceCollection::const_iterator itr = coll.begin();
		itr != coll.end();
		++itr)
	{
		CNdasLogicalDevice* pLogDevice = *itr;

		if (NDAS_LOGICALDEVICE_STATUS_MOUNTED != pLogDevice->GetStatus()) {
			continue;
		}

		CONST NDAS_SCSI_LOCATION& location = pLogDevice->GetNdasScsiLocation();

		BusEnumQuery.InfoClass = INFORMATION_PDO;
		BusEnumQuery.Size = sizeof(BUSENUM_QUERY_INFORMATION);
		BusEnumQuery.SlotNo = location.SlotNo;

		BOOL fSuccess = LsBusCtlQueryInformation(
			&BusEnumQuery,
			sizeof(BUSENUM_QUERY_INFORMATION),
			&BusEnumInformation,
			sizeof(BUSENUM_INFORMATION));

		if (!fSuccess) {
			DPErrorEx(_FT("LanscsiQueryInformation failed at slot %d: "), location.SlotNo);
			continue;
		}

		//
		// Broadcast a primary write access status
		//
		if (ND_ACCESS_ISRW(BusEnumInformation.PdoInfo.GrantedAccess)) {

			const DWORD cbBuffer = 
				sizeof(LSINFOX_HEADER) + sizeof(LSINFOX_PRIMARY_UPDATE);

			BYTE lpbBuffer[cbBuffer] = {0};

			PLSINFOX_HEADER pixHeader = 
				reinterpret_cast<PLSINFOX_HEADER>(lpbBuffer);

			PLSINFOX_DATA pixData = 
				reinterpret_cast<PLSINFOX_DATA>(lpbBuffer + sizeof(LSINFOX_HEADER));

			//
			// CAUTION: InfoExchange Protocol uses little-endian (Intel)
			//

			//
			// Header
			//
			UCHAR ixProtocolName[4] = INFOX_DATAGRAM_PROTOCOL;
			::CopyMemory(pixHeader->Protocol, ixProtocolName, 4);
			pixHeader->LSInfoXMajorVersion = INFOX_DATAGRAM_MAJVER;
			pixHeader->LSInfoXMinorVersion = INFOX_DATAGRAM_MINVER;
			pixHeader->OsMajorType = OSTYPE_WINDOWS;
			
			DWORD dwOSMajorVersion, dwOSMinorVersion;
			pGetOSVersion(&dwOSMajorVersion, &dwOSMinorVersion);
			USHORT usLfsOsMinorType = 
				pInfoXGetOSType(dwOSMajorVersion, dwOSMinorVersion);
			pixHeader->OsMinorType = usLfsOsMinorType;
			pixHeader->Type = LSINFOX_PRIMARY_UPDATE_MESSAGE;
			pixHeader->MessageSize = cbBuffer;

			//
			// Data
			//

			// primary node is dependent to each interface
			pixData->Update.PrimaryNode; 
			pixData->Update.PrimaryPort = LPXRP_LFS_PRIMARY;
			pixData->Update.SWMajorVersion = NDASIX_VERSION_MAJOR; // TODO: Change these values
			pixData->Update.SWMinorVersion = NDASIX_VERSION_MINOR;
			pixData->Update.SWBuildNumber = NDASIX_VERSION_BUILD;
			pixData->Update.NDFSCompatVersion = m_NDFSVersion.wMajor;
			pixData->Update.NDFSVersion = m_NDFSVersion.wMinor;

			//
			// NetDisk Node is a property of each unit device
			//
			pixData->Update.NetDiskNode;
			//
			// We have fixed the port 
			// (CNdasDevice does not store Port Number internally)
			// Do not try to retrieve from GetRemoteLpxAddress()
			//
			pixData->Update.NetDiskPort = NDAS_DEVICE_LPX_PORT;

			//
			// Unit Disk Number is a property of each unit device
			//
			pixData->Update.UnitDiskNo;

			//
			// pLogDevice->GetStatus()
			//
			for (DWORD n = 0; n < pLogDevice->GetUnitDeviceCount(); ++n) {
				//
				// Actually, we should traverse the real entry
				// from the device registrar.
				// However, here we do the shortcut for using NDAS device id
				// and fixed NetDiskPort, etc.
				//
				NDAS_UNITDEVICE_ID unitDeviceId = pLogDevice->GetUnitDeviceID(n);

				_ASSERTE(sizeof(pixData->Update.NetDiskNode) ==
					sizeof(unitDeviceId.DeviceId));

				::CopyMemory(
					pixData->Update.NetDiskNode,
					unitDeviceId.DeviceId.Node,
					sizeof(pixData->Update.NetDiskNode));

				pixData->Update.UnitDiskNo = unitDeviceId.UnitNo;

				//
				// Broadcast the data to every interface
				//
				for (DWORD i = 0; i < MAX_SOCKETLPX_INTERFACE; ++i) {

					if (INVALID_SOCKET != (SOCKET) m_senders[i]) {
						
						//
						// Fill the Primary Node (LPX Address Node)
						//
						LPX_ADDRESS localLpxAddress = 
							m_senders[i].GetBoundAddr()->LpxAddress;

						_ASSERTE(sizeof(pixData->Update.PrimaryNode) ==
							sizeof(localLpxAddress.Node));

						::CopyMemory(
							pixData->Update.PrimaryNode,
							localLpxAddress.Node,
							sizeof(pixData->Update.PrimaryNode));
						
						//
						// Send the data
						//
						DWORD cbSent = 0;
						BOOL fSuccess =	m_senders[i].SendToSync(
								&NDASIX_BCAST_ADDR,
								cbBuffer,
								lpbBuffer,
								0,
								&cbSent);
						if (!fSuccess) {
							DPWarningEx(_FT("Sending a packet failed: "));
						}

					} // end if
				} // for each local LPX address
			} // for each unit device
		} // if the logical device has a primary write access.
	} // for each logical device

	return TRUE;
}
Beispiel #8
0
DWORD
CNdasEventSubscriber::
Run()
{
	_ASSERTE(NULL != m_hDataEvent);
	_ASSERTE(INVALID_HANDLE_VALUE != m_hThread);
	_ASSERTE(NULL != m_hThreadStopEvent);
	// Why?
	// _ASSERTE(m_hThread == ::GetCurrentThread());

	OVERLAPPED ov = {0};
	ov.hEvent = m_hDataEvent;

	BOOL bStopThread = FALSE;
	HANDLE hPipe = WaitServer(bStopThread);

	while (!bStopThread) {

		NDAS_EVENT_MESSAGE message = {0};
		DWORD cbMessage = sizeof(NDAS_EVENT_MESSAGE);
		DWORD cbRead(0);

		BOOL fRead = ::ReadFile(
			hPipe, 
			&message, 
			cbMessage, 
			&cbRead, 
			&ov);

		if (!fRead && ERROR_IO_PENDING != ::GetLastError()) {
			// failure!
			DPErrorEx(_FT("ReadFile failed: "));
			
			::CloseHandle(hPipe);
			hPipe = WaitServer(bStopThread);

			continue;
		}

		if (fRead) {
			::SetEvent(m_hDataEvent);
		}

		HANDLE hWaitHandles[2] = { m_hThreadStopEvent, m_hDataEvent };

		DWORD dwWaitResult = ::WaitForMultipleObjects(
			2,
			hWaitHandles,
			FALSE,
			INFINITE);

		if (WAIT_OBJECT_0 == dwWaitResult) {
			//
			// Thread stop event
			//
			bStopThread = TRUE;

		} else if (WAIT_OBJECT_0 + 1 == dwWaitResult) {

			DWORD cbRead(0);
			BOOL fData = ::GetOverlappedResult(hPipe, &ov, &cbRead, TRUE);

			NDAS_EVENT_INFO eventInfo = {0};
			eventInfo.EventType = message.EventType;
			switch (eventInfo.EventType) {
			case NDAS_EVENT_TYPE_DEVICE_STATUS_CHANGED:
			case NDAS_EVENT_TYPE_DEVICE_PROPERTY_CHANGED:
				eventInfo.DeviceInfo = message.DeviceEventInfo;
				break;
			case NDAS_EVENT_TYPE_UNITDEVICE_PROPERTY_CHANGED:
				eventInfo.UnitDeviceInfo = message.UnitDeviceEventInfo;
				break;
			case NDAS_EVENT_TYPE_LOGICALDEVICE_STATUS_CHANGED:
			case NDAS_EVENT_TYPE_LOGICALDEVICE_DISCONNECTED:
			//case NDAS_EVENT_TYPE_LOGICALDEVICE_RECONNECTING:
			//case NDAS_EVENT_TYPE_LOGICALDEVICE_RECONNECTED:
			//case NDAS_EVENT_TYPE_LOGICALDEVICE_EMERGENCY:
			case NDAS_EVENT_TYPE_LOGICALDEVICE_ALARMED:
				eventInfo.LogicalDeviceInfo = message.LogicalDeviceEventInfo;
				break;
			case NDAS_EVENT_TYPE_LOGICALDEVICE_ENTRY_CHANGED:
			case NDAS_EVENT_TYPE_DEVICE_ENTRY_CHANGED:
			case NDAS_EVENT_TYPE_TERMINATING:
				break;
			case NDAS_EVENT_TYPE_SURRENDER_REQUEST:
				eventInfo.SurrenderRequestInfo = message.SurrenderRequestInfo;
				break;
			case NDAS_EVENT_TYPE_PERIODIC:
			default:
			break;
			}

			if (NDAS_EVENT_TYPE_PERIODIC != message.EventType) {
				CallEventProc(::GetLastError(), &eventInfo);
			}

		} else {

			DPWarningEx(_FT("Wait failed: "));
		}

	}

	if (INVALID_HANDLE_VALUE != hPipe) {
		::CloseHandle(hPipe);
	}

	return 0;
}
Beispiel #9
0
PCNdasDevice
CNdasDeviceRegistrar::Register(const NDAS_DEVICE_ID& DeviceId, DWORD dwSlotNo)
{
	//
	// this will lock this class from here
	// and releases the lock when the function returns;
	//
	ximeta::CAutoLock autolock(this);
	
	DPInfo(_FT("Registering device %s at slot %d\n"), 
		LPCTSTR(CNdasDeviceId(DeviceId)), dwSlotNo);

	// check slot number
	if (dwSlotNo < 1 || dwSlotNo > m_dwMaxSlotNo) {
		::SetLastError(NDASHLPSVC_ERROR_INVALID_SLOT_NUMBER);
		return NULL;
	}

	// check and see if the slot is occupied
	if (m_pbSlotOccupied[dwSlotNo]) {
		::SetLastError(NDASHLPSVC_ERROR_SLOT_ALREADY_OCCUPIED);
		return NULL;
	}

	// find an duplicate address
	if (NULL != Find(DeviceId)) {
		::SetLastError(NDASHLPSVC_ERROR_DUPLICATE_DEVICE_ENTRY);
		return NULL;
	}

	// register
	PCNdasDevice pDevice = new CNdasDevice(dwSlotNo, DeviceId);
	if (NULL == pDevice) {
		// memory allocation failed
		// No need to set error here!
		return NULL;
	}
	pDevice->AddRef();

	BOOL fSuccess = pDevice->Initialize();
	if (!fSuccess) {
//		DebugPrintError((ERROR_T("Device initialization failed!")));
		pDevice->Release();
		return NULL;
	}

	m_pbSlotOccupied[dwSlotNo] = TRUE;

	bool insertResult;

	insertResult = 
		m_deviceSlotMap.insert(DeviceSlotMap::value_type(dwSlotNo, pDevice)).second;
	_ASSERTE(insertResult == true);
	insertResult =	
		m_deviceIdMap.insert(DeviceIdMap::value_type(DeviceId, pDevice)).second;
	_ASSERTE(insertResult == true);

	_ASSERTE(m_deviceSlotMap.size() == m_deviceIdMap.size());

	TCHAR szContainer[30];
	HRESULT hr = ::StringCchPrintf(szContainer, 30, TEXT("Devices\\%04d"), dwSlotNo);
	_ASSERT(SUCCEEDED(hr));

	fSuccess = _NdasSystemCfg.SetSecureValueEx(
		szContainer, 
		_T("DeviceId"), 
		&DeviceId, 
		sizeof(DeviceId));

	if (!fSuccess) {
		DPWarningEx(
			_FT("Writing registration entry to the registry failed at %s.\n"), 
			szContainer);
	}

	//
	// During bootstrapping, we do not publish this event
	// Bootstrapper will do this later.
	//
	if (!m_fBootstrapping) {
		CNdasEventPublisher* pEventPublisher = pGetNdasEventPublisher();
		(VOID) pEventPublisher->DeviceEntryChanged();
	}

	return pDevice;
}