⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mfccddb.cpp

📁 MfcCDDB v1.11 A freeware MFC class to support access to CDDB servers Welcome to MfcCDDB, a collectio
💻 CPP
📖 第 1 页 / 共 5 页
字号:




CCDDB::CCDDB() : m_sProductName(_T("MfcCDDDB")), m_sProductVersion(_T("0.9"))
{
  m_dwLastError = 0;
#ifdef _DEBUG
  m_dwTimeout = 60000; //timeout is set to 60 seconds for debug
#else
  m_dwTimeout = 2000; //2 seconds for release builds
#endif
}

CCDDB::~CCDDB()
{
}

DWORD CCDDB::GetLastError() const 
{ 
  if (m_dwLastError)
    return m_dwLastError; 
  else
    return ::GetLastError();
}

CString CCDDB::GetErrorMessage() const
{
  CString sError;
  if (m_dwLastError)
  {
    TCHAR sMessage[129];
    mciGetErrorString(m_dwLastError, sMessage, 128);
    sError = sMessage;
  }
  else
  {
    //Use the SDK function ::FormatMessage to create a string for us
	  LPTSTR lpBuffer = NULL;
    DWORD dwLastError = ::GetLastError();
    BOOL bSuccess = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE, 
			                              NULL, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT), (LPTSTR) &lpBuffer, 0, NULL);
	  if (bSuccess)
	  {
		  sError = lpBuffer;

      //Don't forget to free the memory ::FormatMessage allocated for us
		  LocalFree(lpBuffer);
	  }
    else
    {
      //Restore the error if FormatMessage failed
      SetLastError(dwLastError);
    }
  }

  return sError;
}

DWORD CCDDB::ComputeDiscID(const CArray<CCDDBTrackPosition, CCDDBTrackPosition&>& tracks)
{
  int nTracks = tracks.GetSize() - 1; //Number of tracks is 1 less than the size
                                      //of the array as it also contains the lead 
                                      //out position
  //Validate our parameters
  ASSERT(nTracks > 0);

  //Iterate across all the tracks
  int n=0;
  for (int i=0; i<nTracks; i++)
  {
    int sum = 0;
    int j = tracks[i].m_nMinute*60 + tracks[i].m_nSecond;
    while (j > 0)
    {
      sum += j%10;
      j /=10;
    }
    n += sum;
  }

  //Compute total track length in seconds
  int t = tracks[nTracks].m_nMinute*60 + tracks[nTracks].m_nSecond - tracks[0].m_nMinute - tracks[0].m_nSecond;

  //Compute DISC ID
  DWORD dwDiscID = ((n % 0xFF) << 24 | t << 8 | nTracks);
  return dwDiscID;
}

BOOL CCDDB::GetTrackPositions(CArray<CCDDBTrackPosition, CCDDBTrackPosition&>& tracks, LPCTSTR pszDrive)
{
  //Remove any tracks already in the array
  tracks.RemoveAll();

  //Open the specified "cdaudio" MCI device
  MCI_OPEN_PARMS mciOpenParms;
  mciOpenParms.lpstrDeviceType = _T("cdaudio");
  mciOpenParms.lpstrElementName = pszDrive;
  m_dwLastError = ::mciSendCommand(0, MCI_OPEN, MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE | (pszDrive ? MCI_OPEN_ELEMENT : 0), (DWORD) &mciOpenParms);
  if (m_dwLastError)
  {
    TRACE(_T("Failed to open the cdaudio MCI device, GetLastError:%d, %s\n"), GetLastError(), GetErrorMessage());
    return FALSE;
  }

  //Set the time format to Minute/Second/Frame (MSF) format
  MCI_SET_PARMS mciSetParms;
  mciSetParms.dwTimeFormat = MCI_FORMAT_MSF;
  m_dwLastError = ::mciSendCommand(mciOpenParms.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD) &mciSetParms);
  if (m_dwLastError)
  {
    //Dont forget to close the MCI device
    ::mciSendCommand(mciOpenParms.wDeviceID, MCI_CLOSE, 0, 0);

    TRACE(_T("Failed to set cdaudio MCI device to MSF format, GetLastError:%d, %s\n"), GetLastError(), GetErrorMessage());
    return FALSE;
  }

  //Get the total track count
  MCI_STATUS_PARMS mciStatusParms;
  mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
  m_dwLastError = ::mciSendCommand(mciOpenParms.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD) &mciStatusParms);
  if (m_dwLastError)
  {
    //Dont forget to close the MCI device
    ::mciSendCommand(mciOpenParms.wDeviceID, MCI_CLOSE, 0, 0);

    TRACE(_T("Failed to get number of cdaudio tracks, GetLastError:%d, %s\n"), GetLastError(), GetErrorMessage());
    return FALSE;
  }

  //Iterate through all the tracks getting their starting position
  int nTotalTracks = (int) mciStatusParms.dwReturn;
  tracks.SetSize(nTotalTracks + 1);
  for (int i=1; i<=nTotalTracks; i++)
  {                      
    mciStatusParms.dwItem = MCI_STATUS_POSITION;
    mciStatusParms.dwTrack = i;
    m_dwLastError = mciSendCommand(mciOpenParms.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD) &mciStatusParms);
    if (m_dwLastError)
    {
      //Dont forget to close the MCI device
      ::mciSendCommand(mciOpenParms.wDeviceID, MCI_CLOSE, 0, 0);

      //Remove all the fields if we have an error getting any of the tracks
      tracks.RemoveAll();

      TRACE(_T("Failed to get track %d's starting position, GetLastError:%d, %s\n"), GetLastError(), GetErrorMessage());
      return FALSE;
    }

    //Save the track position in MSF format
    CCDDBTrackPosition trackPosition;
    trackPosition.m_nMinute = MCI_MSF_MINUTE(mciStatusParms.dwReturn);
    trackPosition.m_nSecond = MCI_MSF_SECOND(mciStatusParms.dwReturn);
    trackPosition.m_nFrame  = MCI_MSF_FRAME(mciStatusParms.dwReturn);

    //Store the value in the array
    tracks.SetAt(i-1, trackPosition);
  }

  //Get the last track's length
  mciStatusParms.dwItem = MCI_STATUS_LENGTH;
  mciStatusParms.dwTrack = nTotalTracks;
  m_dwLastError = mciSendCommand(mciOpenParms.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD) &mciStatusParms);
  if (m_dwLastError)
  {
    //Dont forget to close the MCI device
    ::mciSendCommand(mciOpenParms.wDeviceID, MCI_CLOSE, 0, 0);

    //Remove all the fields if we have an error getting any of the tracks
    tracks.RemoveAll();

    TRACE(_T("Failed to get track %d's length, GetLastError:%d, %s\n"), GetLastError(), GetErrorMessage());
    return FALSE;
  }


  //Compute lead-out track position

  DWORD dwLenM = MCI_MSF_MINUTE(mciStatusParms.dwReturn);
  DWORD dwLenS = MCI_MSF_SECOND(mciStatusParms.dwReturn);
  DWORD dwLenF = MCI_MSF_FRAME(mciStatusParms.dwReturn) + 1; //Fix MCI Windows bug according to CDDB Howto doc
  DWORD dwPosM = tracks[nTotalTracks-1].m_nMinute;
  DWORD dwPosS = tracks[nTotalTracks-1].m_nSecond;
  DWORD dwPosF = tracks[nTotalTracks-1].m_nFrame;

  //Compute lead out track position (in frame format)
  DWORD dwPos = (dwPosM*60*75) + (dwPosS*75) + dwPosF + (dwLenM*60*75) + (dwLenS*75) + dwLenF;

  //Convert dwPos back to MSF format
  CCDDBTrackPosition trackPosition;
  trackPosition.m_nFrame = dwPos % 75;
  dwPos /= 75;
  trackPosition.m_nSecond = dwPos % 60;
  dwPos /= 60;
  trackPosition.m_nMinute = dwPos;

  //And store in the array
  tracks.SetAt(nTotalTracks, trackPosition);

  //Dont forget to close the MCI device
  ::mciSendCommand(mciOpenParms.wDeviceID, MCI_CLOSE, 0, 0);

  return TRUE;
}

BOOL CCDDB::ComputeDiscID(DWORD& dwDiscID, LPCTSTR pszDrive)
{
  //Get the track details
  CArray<CCDDBTrackPosition, CCDDBTrackPosition&> tracks;
  if (!GetTrackPositions(tracks, pszDrive))
    return FALSE;
  
  //Compute the DISC ID now that we have got all the track information
  dwDiscID = ComputeDiscID(tracks);
  
  return TRUE;    
}
    
void CCDDB::GetCDROMDrives(CStringArray& drives)
{
  //empty out the array
  drives.RemoveAll();

  //Iterate across all the drive letters to find out which ones are CDROMs
  for (int i=1; i<=26; i++)
  {
    CString sDrive;
    sDrive.Format(_T("%c:"), i-1+'A');
    if (GetDriveType(sDrive) == DRIVE_CDROM)
      drives.Add(sDrive);
  }
}

BOOL CCDDB::ReadResponse(CHTTPSocket& socket, LPSTR pszBuffer, int nInitialBufSize, LPSTR pszTerminator, LPSTR* ppszOverFlowBuffer, int nGrowBy, DWORD dwHint)
{
  ASSERT(ppszOverFlowBuffer);          //Must have a valid string pointer
  ASSERT(*ppszOverFlowBuffer == NULL); //Initially it must point to a NULL string

  //The local variables which will receive the data
  LPSTR pszRecvBuffer = pszBuffer;
  int nBufSize = nInitialBufSize;
  
  //Retrieve the reponse using until we
	//get the terminator or a timeout occurs
	BOOL bFoundTerminator = FALSE;
  BOOL bContinue = TRUE;
	int nReceived = 0;
	DWORD dwStartTicks = ::GetTickCount();
	while (!bFoundTerminator && bContinue)
	{
		//Has the timeout occured
		if ((::GetTickCount() - dwStartTicks) >	m_dwTimeout)
		{
      if (pszRecvBuffer && nReceived)
      {
		    pszRecvBuffer[nReceived] = '\0';
        m_sLastCommandResponse = pszRecvBuffer; //Hive away the last command reponse
      }
      SetLastError(WSAETIMEDOUT);
      m_dwLastError = 0;
			return FALSE;
		}

    //check the socket for readability
    BOOL bReadible;
    if (!socket.IsReadible(bReadible))
    {
      if (pszRecvBuffer && nReceived)
      {
	      pszRecvBuffer[nReceived] = '\0';
        m_sLastCommandResponse = pszRecvBuffer; //Hive away the last command reponse
      }
      m_dwLastError = 0;
			return FALSE;
    }
    else if (!bReadible) //no data to receive, just loop around
    {
      Sleep(250); //Sleep for a while before we loop around again
      continue;
    }

		//receive the data from the socket
    int nBufRemaining = nBufSize-nReceived-1; //Allows allow one space for the NULL terminator
    if (nBufRemaining<0)
      nBufRemaining = 0;
	  int nData = socket.Receive(pszRecvBuffer+nReceived, nBufRemaining);

    if (nData)
    {
      //Reset the idle timeout if data was received
			dwStartTicks = ::GetTickCount();

      //Increment the count of data received
		  nReceived += nData;							   
    }

    //If an error occurred receiving the data
		if (nData == SOCKET_ERROR)
		{
      //NULL terminate the data received
      if (pszRecvBuffer)
		    pszBuffer[nReceived] = '\0';

      m_dwLastError = 0;
      m_sLastCommandResponse = pszRecvBuffer; //Hive away the last command reponse
		  return FALSE; 
		}
		else
		{
      //NULL terminate the data received
      if (pszRecvBuffer)
		    pszRecvBuffer[nReceived] = '\0';

      if (nBufRemaining-nData == 0) //No space left in the current buffer
      {
        //Allocate the new receive buffer
        nBufSize += nGrowBy; //Grow the buffer by the specified amount
        LPSTR pszNewBuf = new char[nBufSize];   
        pszNewBuf[0] = '\0'; //Initially NULL terminate the data

        //copy the old contents over to the new buffer and assign 
        //the new buffer to the local variable used for Retrieveing 
        //from the socket
        if (pszRecvBuffer)
          strcpy(pszNewBuf, pszRecvBuffer);
        pszRecvBuffer = pszNewBuf;

        //delete the old buffer if it was allocated
        if (*ppszOverFlowBuffer)
          delete [] *ppszOverFlowBuffer;
        
        //Remember the overflow buffer for the next time around
        *ppszOverFlowBuffer = pszNewBuf;        
      }
		}

    //Special case code for reading a Query response
    if (dwHint == READ_RESPONSE_QUERY)
    {
      if (pszRecvBuffer && strlen(pszRecvBuffer))
      {
        //Extract the HTTP body from the response;
        LPSTR pszBody = FindHTTPBody(pszRecvBuffer);

        //From the HTTP body get the CDDB response code
        int nResponseCode = GetCDDBReponseCode(pszBody);

        //Only continue to receive data if the response code indicates more data is to be received
        if ((nResponseCode < 210 || nResponseCode > 219))
          bContinue = FALSE;          
      }
    }
    else if (dwHint == READ_RESPONSE_SUBMIT)    //Special case code for reading a submit response
    {
      if (pszRecvBuffer && strlen(pszRecvBuffer))
      {
        //Extract the HTTP body from the response;
        LPSTR pszBody = FindHTTPBody(pszRecvBuffer);

        //From the HTTP body get the CDDB response code
        int nResponseCode = GetCDDBReponseCode(pszBody);

        //Only continue to receive data if the response code indicates more data is to be received
        if ((nResponseCode < 210 || nResponseCode > 219))
          bContinue = FALSE;          
      }
    }


    //Check to see if the terminator character(s) have been found
		bFoundTerminator = (strstr(pszRecvBuffer, pszTerminator) != NULL);
	}

	//Remove the terminator from the response data
  if (bFoundTerminator)
    pszRecvBuffer[nReceived - strlen(pszTerminator)] = '\0';

  return TRUE;
}

CString CCDDB::GetUserName()
{
  //Get the user name 
  TCHAR sComputerName[_MAX_PATH];
  DWORD dwSize = _MAX_PATH;
  ::GetUserName(sComputerName, &dwSize);

  return sComputerName;
}

CString CCDDB::GetHostName()
{
  CString sHost;
  char pszHost[_MAX_PATH];
  if (gethostname(pszHost, _MAX_PATH) == 0)
    sHost = pszHost;
  return sHost;
}

CString CCDDB::GetHelloCommand()
{
  CString sCommand;
  sCommand.Format(_T("hello=%s+%s+%s+%s&proto=4"), GetUserName(), GetHostName(), m_sProductName, m_sProductVersion);
  return sCommand;
}

LPSTR CCDDB::FindHTTPBody(LPCSTR pszResponse)
{
  //Validate our parameters
  ASSERT(pszResponse);
  ASSERT(strlen(pszResponse));

  //Find the HTTP body
  LPSTR pszData = strstr(pszResponse, "\r\n\r\n");
  
  //If found, skip over the 2 lines
  if (pszData)
    pszData += 4;

  return pszData;  
}

LPSTR CCDDB::SkipToNextLine(LPSTR pszLine)
{
  //Validate our parameters
  ASSERT(pszLine);

  //Find the next line. Both Dos (\r\n) 
  //and Unix (\n) terminators are valid. First try to
  //find a DOS EOL
  LPSTR lpszData = strstr(pszLine, "\r\n");
  if (lpszData)
    lpszData += 2;
  else
  {
    lpszData = strstr(pszLine, "\n");
    if (lpszData)
      lpszData++;
  }

  return lpszData;  
}

LPSTR CCDDB::GetNextLine(LPSTR pszLine)
{
  //validate our parameters
  ASSERT(pszLine);
  ASSERT(strlen(pszLine));

  //Find the start of the next line. Both Dos (\r\n) 
  //and Unix (\n) terminators are valid. First try to
  //find a DOS EOL

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -