BOOL CNTEventLogSource::Uninstall(LPCTSTR lpszLogName, LPCTSTR lpszSourceName)
{
  //Validate our parameters
  ATLASSUME(lpszLogName != NULL);
  ATLASSERT(_tcslen(lpszLogName));
  ATLASSUME(lpszSourceName != NULL);
  ATLASSERT(_tcslen(lpszSourceName));

  //Remove the settings from the registry
  TCHAR szSubKey[4096];
  _stprintf_s(szSubKey, sizeof(szSubKey)/sizeof(TCHAR), _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\%s\\%s"), lpszLogName, lpszSourceName);
  long nSuccess = RegDeleteKey(HKEY_LOCAL_MACHINE, szSubKey);
  if (nSuccess != ERROR_SUCCESS) //If we cannot delete this registry key, then abort this function before we go any further
  {
    SetLastError(nSuccess); //Make the last error value available to our callers 
    return FALSE;
  }

  //Remove ourself from the "Sources" registry key
  _stprintf_s(szSubKey, sizeof(szSubKey)/sizeof(TCHAR), _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\%s"), lpszLogName);
  ATL::CRegKey appKey;
  if (appKey.Open(HKEY_LOCAL_MACHINE, szSubKey, KEY_WRITE | KEY_READ) == ERROR_SUCCESS)
  {
    CNTServiceStringArray sources;
    if (GetStringArrayFromRegistry(appKey, _T("Sources"), sources))
    {
      //If our name is in the array then remove it
      BOOL bFoundMyself = FALSE;

    #ifdef CNTSERVICE_MFC_EXTENSIONS
      for (int i=0; i<sources.GetSize() && !bFoundMyself; i++)
      {
        bFoundMyself = (sources.GetAt(i) == lpszSourceName);
        if (bFoundMyself)
        {
          sources.RemoveAt(i);
        }
      }
    #else
      CNTServiceStringArray::iterator iterFind = std::find(sources.begin(), sources.end(), lpszSourceName);
      bFoundMyself = (iterFind != sources.end());
      if (bFoundMyself)
        sources.erase(iterFind);
    #endif

      if (bFoundMyself)
        SetStringArrayIntoRegistry(appKey, _T("Sources"), sources);
    }
  }

  return TRUE;
}
BOOL CNTScmService::QueryStatus(SERVICE_STATUS& ServiceStatus) const
{
  //Validate our parameters
  ATLASSUME(m_hService != NULL);
  
  return QueryServiceStatus(m_hService, &ServiceStatus);
}
BOOL CNTScmService::Start(DWORD dwNumServiceArgs,	LPCTSTR* lpServiceArgVectors) const
{
  //Validate our parameters
  ATLASSUME(m_hService != NULL);
  
  return StartService(m_hService, dwNumServiceArgs, lpServiceArgVectors);
}
BOOL CNTServiceControlManager::EnumServices(DWORD dwServiceType, DWORD dwServiceState, void* pUserData, ENUM_SERVICES_PROC lpEnumServicesFunc) const
{
  //Validate our parameters
  ATLASSUME(m_hSCM != NULL);

  DWORD dwBytesNeeded;
  DWORD dwServices;
  DWORD dwResumeHandle = 0;
  BOOL bSuccess = EnumServicesStatus(m_hSCM, dwServiceType, dwServiceState, NULL, 0, &dwBytesNeeded, &dwServices, &dwResumeHandle);
  if (!bSuccess && GetLastError() == ERROR_MORE_DATA)
  {
    //Allocate some memory for the API
    ATL::CHeapPtr<BYTE> lpBuffer;
    if (!lpBuffer.Allocate(dwBytesNeeded))
    {
      SetLastError(ERROR_OUTOFMEMORY);
      return FALSE;
    }
  
    LPENUM_SERVICE_STATUS lpServices = reinterpret_cast<LPENUM_SERVICE_STATUS>(lpBuffer.m_pData);
    DWORD dwSize = 0;
    bSuccess = EnumServicesStatus(m_hSCM, dwServiceType, dwServiceState, (LPENUM_SERVICE_STATUS) lpServices, dwBytesNeeded, &dwSize, &dwServices, &dwResumeHandle);
    if (bSuccess)
    {
      BOOL bContinue = TRUE;
      for (DWORD i=0; i<dwServices && bContinue; i++)
        bContinue = lpEnumServicesFunc(pUserData, lpServices[i]);
    }
  }
  return bSuccess;
}
BOOL CNTScmService::Delete() const
{
  //Validate our parameters
  ATLASSUME(m_hService != NULL);
  
  return DeleteService(m_hService);
}
BOOL CNTScmService::EnumDependents(DWORD dwServiceState, void* pUserData, ENUM_SERVICES_PROC lpEnumServicesFunc) const
{
  //Validate our parameters
  ATLASSUME(m_hService != NULL);

  DWORD dwBytesNeeded;
  DWORD dwServices;
  BOOL bSuccess = EnumDependentServices(m_hService, dwServiceState, NULL, 0, &dwBytesNeeded, &dwServices);
  DWORD dwLastError = GetLastError();
  if (!bSuccess && (dwLastError == ERROR_MORE_DATA) || (dwLastError == ERROR_INSUFFICIENT_BUFFER)) //Note we use ERROR_INSUFFICIENT_BUFFER here even though it is not documented as a legal return value from EnumDependentServices here
  {
    //Allocate some memory for the API
    ATL::CHeapPtr<ENUM_SERVICE_STATUS> lpServices;
    if (!lpServices.AllocateBytes(dwBytesNeeded))
    {
      SetLastError(ERROR_OUTOFMEMORY);
      return FALSE;
    }

    DWORD dwSize;
    bSuccess = EnumDependentServices(m_hService, dwServiceState, lpServices.m_pData, dwBytesNeeded, &dwSize, &dwServices);
    if (bSuccess)
    {
      BOOL bContinue = TRUE;
      for (DWORD i=0; i<dwServices && bContinue; i++)
        bContinue = lpEnumServicesFunc(pUserData, lpServices[i]);
    }
  }
  return bSuccess;
}
BOOL CNTScmService::Interrogate() const
{
  //Validate our parameters
  ATLASSUME(m_hService != NULL);
  
  SERVICE_STATUS ServiceStatus;
  return ControlService(m_hService, SERVICE_CONTROL_INTERROGATE, &ServiceStatus);
}
BOOL CNTScmService::Continue() const
{
  //Validate our parameters
  ATLASSUME(m_hService != NULL);
  
  SERVICE_STATUS ServiceStatus;
  return ControlService(m_hService, SERVICE_CONTROL_CONTINUE, &ServiceStatus);
}
BOOL CNTServiceControlManager::Lock()
{
  //Validate our parameters
  ATLASSUME(m_hSCM != NULL);

  m_hLock = LockServiceDatabase(m_hSCM);
  return (m_hLock != NULL);
}
BOOL CNTScmService::Control(DWORD dwControl)
{
  //Validate our parameters
  ATLASSUME(m_hService != NULL);
  
  SERVICE_STATUS ServiceStatus;
  return ControlService(m_hService, dwControl, &ServiceStatus);
}
BOOL CNTScmService::SetObjectSecurity(SECURITY_INFORMATION dwSecurityInformation,
                                      PSECURITY_DESCRIPTOR lpSecurityDescriptor) const

{
  //Validate our parameters
  ATLASSUME(m_hService != NULL);
  
  return SetServiceObjectSecurity(m_hService, dwSecurityInformation, lpSecurityDescriptor);
}
BOOL CNTServiceControlManager::OpenService(LPCTSTR lpServiceName,	DWORD dwDesiredAccess, CNTScmService& service) const
{
  //Validate our parameters
  ATLASSUME(m_hSCM != NULL);

  SC_HANDLE hService = ::OpenService(m_hSCM, lpServiceName, dwDesiredAccess);
  if (hService != NULL)
    service.Attach(hService);
  return (hService != NULL);
}
BOOL CTuoImage::BitBlt(HDC hDestDC, int xDest, int yDest, int nDestWidth, int nDestHeight, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight) const
{
	ATLASSUME(m_hBitmap != NULL);
	ATLASSERT(hDestDC != NULL);
	ATLASSERT(nDestWidth > 0);
	ATLASSERT(nDestHeight > 0);
	ATLASSERT(nSrcWidth > 0);
	ATLASSERT(nSrcHeight > 0);

	::SelectObject(m_hCurrentThreadDC, m_hBitmap);
	return ::StretchBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight, m_hCurrentThreadDC, xSrc, ySrc, nSrcWidth, nSrcHeight, SRCCOPY);
}
BOOL CNTScmService::ChangeConfig(DWORD dwServiceType,	DWORD dwStartType,
                                 DWORD dwErrorControl, LPCTSTR lpBinaryPathName,
                                 LPCTSTR lpLoadOrderGroup, LPDWORD lpdwTagId,
                                 LPCTSTR lpDependencies, LPCTSTR lpServiceStartName,
                                 LPCTSTR lpPassword, LPCTSTR lpDisplayName) const
{
  //Validate our parameters
  ATLASSUME(m_hService != NULL);
  
  return ChangeServiceConfig(m_hService, dwServiceType, dwStartType,
                             dwErrorControl, lpBinaryPathName, lpLoadOrderGroup, lpdwTagId,
                             lpDependencies, lpServiceStartName, lpPassword, lpDisplayName);
}
BOOL CNTScmService::QueryConfig(LPQUERY_SERVICE_CONFIG& lpServiceConfig) const
{
  //Validate our parameters
  ATLASSUME(m_hService != NULL);
  ATLASSERT(lpServiceConfig == NULL); //To prevent double overwrites, this function
                                      //asserts if you do not send in a NULL pointer

  DWORD dwBytesNeeded;
  BOOL bSuccess = QueryServiceConfig(m_hService, NULL, 0, &dwBytesNeeded);
  if (!bSuccess && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  {
    lpServiceConfig = reinterpret_cast<LPQUERY_SERVICE_CONFIG>(new BYTE[dwBytesNeeded]);
    DWORD dwSize;
    bSuccess = QueryServiceConfig(m_hService, lpServiceConfig, dwBytesNeeded, &dwSize);
  }
  return bSuccess;
}
BOOL CNTServiceControlManager::QueryLockStatus(LPQUERY_SERVICE_LOCK_STATUS& lpLockStatus) const
{
  //Validate our parameters
  ATLASSUME(m_hSCM != NULL);
  ATLASSERT(lpLockStatus == NULL); //To prevent double overwrites, this function
                                //asserts if you do not send in a NULL pointer

  DWORD dwBytesNeeded;
  BOOL bSuccess = QueryServiceLockStatus(m_hSCM, NULL, 0, &dwBytesNeeded);
  if (!bSuccess && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  {
    lpLockStatus = reinterpret_cast<LPQUERY_SERVICE_LOCK_STATUS>(new BYTE[dwBytesNeeded]);
    DWORD dwSize;
    bSuccess = QueryServiceLockStatus(m_hSCM, lpLockStatus, dwBytesNeeded, &dwSize);
  }
  return bSuccess;
}
BOOL CNTScmService::QueryObjectSecurity(SECURITY_INFORMATION dwSecurityInformation,
                                        PSECURITY_DESCRIPTOR& lpSecurityDescriptor) const
{
  //Validate our parameters
  ATLASSUME(m_hService != NULL);
  ATLASSERT(lpSecurityDescriptor == NULL); //To prevent double overwrites, this function
                                        //asserts if you do not send in a NULL pointer

  DWORD dwBytesNeeded;
  BOOL bSuccess = QueryServiceObjectSecurity(m_hService, dwSecurityInformation, NULL, 0, &dwBytesNeeded);
  if (!bSuccess && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  {
    lpSecurityDescriptor = static_cast<PSECURITY_DESCRIPTOR>(new BYTE[dwBytesNeeded]);
    DWORD dwSize;
    bSuccess = QueryServiceObjectSecurity(m_hService, dwSecurityInformation, lpSecurityDescriptor, dwBytesNeeded, &dwSize);
  }

  return bSuccess;
}
BOOL CTuoImage::Draw(HDC hDestDC, int xDest, int yDest, int nDestWidth, int nDestHeight, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight) const
{
	ATLASSUME(m_hBitmap != NULL);
	ATLASSERT(hDestDC != NULL);
	ATLASSERT(nDestWidth > 0);
	ATLASSERT(nDestHeight > 0);
	ATLASSERT(nSrcWidth > 0);
	ATLASSERT(nSrcHeight > 0);

	::SelectObject(m_hCurrentThreadDC, m_hBitmap);

	if (m_bUseBitblt)
		return ::StretchBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight, m_hCurrentThreadDC, xSrc, ySrc, nSrcWidth, nSrcHeight, SRCCOPY);

	BLENDFUNCTION bf;
	bf.BlendOp = AC_SRC_OVER;
	bf.BlendFlags = 0;
	bf.SourceConstantAlpha = 0xff;
	bf.AlphaFormat = AC_SRC_ALPHA;
	return ::AlphaBlend(hDestDC, xDest, yDest, nDestWidth, nDestHeight, m_hCurrentThreadDC, xSrc, ySrc, nSrcWidth, nSrcHeight, bf);
}
BOOL CNTEventLogSource::Report(WORD wType, WORD wCategory, DWORD dwEventID, PSID lpUserSid,
                               WORD wNumStrings, DWORD dwDataSize, LPCTSTR* lpStrings, LPVOID lpRawData)
{
  ATL::CComCritSecLock<ATL::CComAutoCriticalSection> sl(m_csReport, true);

  ATLASSERT(m_hEventSource == NULL);
#ifdef CNTSERVICE_MFC_EXTENSIONS
  if (!Register(m_sServerName, m_sSourceName))
#else
  if (!Register(m_sServerName.c_str(), m_sSourceName.c_str()))
#endif
    return FALSE;
  ATLASSUME(m_hEventSource != NULL);

  //Call the SDK version of the function
  BOOL bSuccess = ReportEvent(m_hEventSource, wType,	wCategory, dwEventID, lpUserSid,
                              wNumStrings, dwDataSize, lpStrings, lpRawData);
  Deregister();  

  return bSuccess;
}
CEventLogRecord::CEventLogRecord(const EVENTLOGRECORD* pRecord)
{
  //Validate our parameters
  ATLASSUME(pRecord != NULL);

  //First the easy ones
  m_dwRecordNumber = pRecord->RecordNumber;
  m_dwTimeGenerated = pRecord->TimeGenerated;
  m_dwTimeWritten = pRecord->TimeWritten;
  m_dwEventID = pRecord->EventID;
  m_wEventType = pRecord->EventType;
  m_wEventCategory = pRecord->EventCategory;

  //Copy over the SID
  DWORD i = 0;
  const BYTE* pBeginRecord = reinterpret_cast<const BYTE*>(pRecord);
  ATLASSUME(pBeginRecord != NULL);
  DWORD dwCurOffset = pRecord->UserSidOffset;
  if (pRecord->UserSidLength)
  {
  #ifdef CNTSERVICE_MFC_EXTENSIONS
    m_UserSID.SetSize(0, pRecord->UserSidLength); //Preallocate the array to improve performance
  #else
    m_UserSID.reserve(pRecord->UserSidLength); //Preallocate the array to improve performance
  #endif
  }
  while (i < pRecord->UserSidLength)
  {
  #ifdef CNTSERVICE_MFC_EXTENSIONS
    m_UserSID.Add(pBeginRecord[dwCurOffset + i]);
  #else
    m_UserSID.push_back(pBeginRecord[dwCurOffset + i]);
  #endif
    i++;
  }
  dwCurOffset += pRecord->UserSidLength;

  //Copy over the Binary data
  i = 0;
  dwCurOffset = pRecord->DataOffset;
  if (pRecord->DataLength)
  {
  #ifdef CNTSERVICE_MFC_EXTENSIONS
    m_Data.SetSize(0, pRecord->DataLength); //Preallocate the array to improve performance
  #else
    m_Data.reserve(pRecord->DataLength); //Preallocate the array to improve performance
  #endif
  }
  while (i < pRecord->DataLength)
  {
  #ifdef CNTSERVICE_MFC_EXTENSIONS
    m_Data.Add(pBeginRecord[dwCurOffset + i]);
  #else
    m_Data.push_back(pBeginRecord[dwCurOffset + i]);
  #endif
    i++;
  }
  dwCurOffset += pRecord->DataLength;

  //Copy over the SourceName
  const TCHAR* pszBeginRecord = reinterpret_cast<const TCHAR*>(pBeginRecord + sizeof(EVENTLOGRECORD));
  dwCurOffset = 0;
  DWORD dwStartOffset = dwCurOffset;
  while (pszBeginRecord[dwCurOffset])
    dwCurOffset++;
  m_sSourceName = &pszBeginRecord[dwStartOffset];

  //Skip over the NULL 
  dwCurOffset++;

  //Copy over the ComputerName
  dwStartOffset = dwCurOffset;
  while (pszBeginRecord[dwCurOffset])
    dwCurOffset++;
  m_sComputerName = &pszBeginRecord[dwStartOffset];

  //Copy over the strings array
  int nStringsRead = 0;
  pszBeginRecord = reinterpret_cast<const TCHAR*>(pBeginRecord + pRecord->StringOffset);
  dwCurOffset = 0;
  while (nStringsRead < pRecord->NumStrings)
  {
    //Find the next string
    dwStartOffset = dwCurOffset;
    while (pszBeginRecord[dwCurOffset])
      dwCurOffset++;

    //Add it to the array
    CNTServiceString sText(&pszBeginRecord[dwStartOffset]);
  #ifdef CNTSERVICE_MFC_EXTENSIONS
    m_Strings.Add(sText);
  #else
    m_Strings.push_back(sText);
  #endif

    //Increment the number of strings read
    nStringsRead++;

    //Skip over the NULL
    dwCurOffset++;
  }
}
BOOL CNTEventLogSource::Install(LPCTSTR lpszLogName, LPCTSTR lpszSourceName, LPCTSTR lpszEventMessageFile, LPCTSTR pszEventCategoryMessageFile, LPCTSTR pszEventParameterMessageFile, DWORD dwTypesSupported, DWORD dwCategoryCount)
{
  //Validate our parameters
  ATLASSUME(lpszLogName != NULL);
  ATLASSERT(_tcslen(lpszLogName));
  ATLASSUME(lpszSourceName != NULL);
  ATLASSERT(_tcslen(lpszSourceName));
  ATLASSUME(lpszEventMessageFile != NULL);

  //What will be the return value from this function, assume the worst
  BOOL bSuccess = FALSE;

  //Make the necessary updates to the registry
  TCHAR szKey[4096];
  _stprintf_s(szKey, sizeof(szKey)/sizeof(TCHAR), _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\%s"), lpszLogName);
  ATL::CRegKey appKey;
  if (appKey.Create(HKEY_LOCAL_MACHINE, szKey) == ERROR_SUCCESS)
  {
    ATL::CRegKey sourceKey;
    if (sourceKey.Create(appKey, lpszSourceName, REG_NONE, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_READ, NULL) == ERROR_SUCCESS)
    {
      //Write the EventMessageFile string value
      bSuccess = sourceKey.SetStringValue(_T("EventMessageFile"), lpszEventMessageFile) == ERROR_SUCCESS;

      //Write the TypesSupported value
      bSuccess = bSuccess && sourceKey.SetDWORDValue(_T("TypesSupported"), dwTypesSupported) == ERROR_SUCCESS;

      //Write the CategoryCount value if required
      if (dwCategoryCount && bSuccess)
        bSuccess = sourceKey.SetDWORDValue(_T("CategoryCount"), dwCategoryCount) == ERROR_SUCCESS;

      //Write the CategoryMessageFile string if required
      if (pszEventCategoryMessageFile && _tcslen(pszEventCategoryMessageFile) && bSuccess)
        bSuccess = sourceKey.SetStringValue(_T("CategoryMessageFile"), pszEventCategoryMessageFile) == ERROR_SUCCESS;

      //Write the ParameterMessageFile string if required
      if (pszEventParameterMessageFile && _tcslen(pszEventParameterMessageFile) && bSuccess)
        bSuccess = sourceKey.SetStringValue(_T("ParameterMessageFile"), pszEventParameterMessageFile) == ERROR_SUCCESS;

      //Update the sources registry key so that the event viewer can filter on the events which we write to the event log
      if (bSuccess)
      {
        CNTServiceStringArray sources;
        if (GetStringArrayFromRegistry(appKey, _T("Sources"), sources))
        {
          //If our name is not in the array then add it
          BOOL bFoundMyself = FALSE;
        #ifdef CNTSERVICE_MFC_EXTENSIONS
          for (int i=0; i<sources.GetSize() && !bFoundMyself; i++)
            bFoundMyself = (sources.GetAt(i) == lpszSourceName);
        #else
          bFoundMyself = (std::find(sources.begin(), sources.end(), lpszSourceName) != sources.end());
        #endif
          if (!bFoundMyself)
          {
          #ifdef CNTSERVICE_MFC_EXTENSIONS
            sources.Add(lpszSourceName);
          #else
            sources.push_back(lpszSourceName);
          #endif
            SetStringArrayIntoRegistry(appKey, _T("Sources"), sources);
          }
        }
      }
    }
  }

  return bSuccess;
}