?? mfccddb.cpp
字號(hào):
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
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -