// Created by Larry Leonard, Definitive Solutions, Inc. #include "stdafx.h" #include "GenericAtl.h" #include #include ///////////////////////////////////////////////////////////////////////////// // Statics MyAtlLog GenericAtl::TheLog; ///////////////////////////////////////////////////////////////////////////// // GenericAtl // Returns size in bytes, or zero to mean empty of non-existant. /* static */ _fsize_t GenericAtl::GetFileSize(const string& strPath) { ATLASSERT(! strPath.empty()); _finddata_t fi = { sizeof(fi) }; long lnHandle(_findfirst(strPath.data(), &fi)); if (-1 == lnHandle) { DOMYLOG (SEV_WARNS, "Cannot find file %s on disk!", strPath.data()); } _ASSERTE(0 <= fi.size && "File size is negative"); return fi.size; } // Delete a file. /* static */ BOOL GenericAtl::RemoveFile(const string& strPath) { ATLASSERT(! strPath.empty()); return DeleteFile(strPath.data()); } // Parse full path down to just filename. /* static */ string GenericAtl::FileFromPath(const string& strPath) { ATLASSERT(! strPath.empty()); string strFile(strPath); int nIndex(strFile.rfind('\\')); if (-1 != nIndex) { strFile = strFile.substr(nIndex + 1); } return strFile; } // Format the system error number returned by ::GetLastError() to a string. /* static */ const char* GenericAtl::GetSysErrorString() { static string str; // There is an error number still. if (0 != GetLastError()) { LPVOID lpMsgBuf = NULL; if (::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, // Flags. NULL, // Message source. GetLastError(), // Message id. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language. (LPTSTR) &lpMsgBuf, // Buffer to receive. 0, // Max size of buffer. NULL // Argument list. )) { // Remove goofy final characters. str = reinterpret_cast (lpMsgBuf); str = str.substr(0, str.length() - 2); } else { DOMYLOG (SEV_ERROR, "\n::FormatMessage() failed with error <%d>.", GetLastError()); } VERIFY(NULL == ::LocalFree(lpMsgBuf)); } else { // No error number. We must not return the default string, // "The operation completed successfully.", because that confuses the user. // Calling CString::LoadString() from here always fails with errno 1814: // "The specified resource name cannot be found in the image file." } return str.data(); } // Obtain the error message for a given HRESULT. /* static */ const char* GenericAtl::GetHrErrorString(HRESULT hr) { LPVOID lpMsgBuf = NULL; static string str; if (::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL)) { // Remove goofy final characters. str = static_cast (lpMsgBuf); str = str.substr(0, str.length() - 2); } else { DOMYLOG (SEV_ERROR, "\n::FormatMessage() failed with error <%d>.\n", GetLastError()); } // Free the buffer. VERIFY(NULL == ::LocalFree(lpMsgBuf)); return str.data(); } // Get the parse error number and string. /* static */ const char* GenericAtl::GetXMLDOMErrorString( const IXMLDOMDocumentPtr& spXMLDOMDocument) { IXMLDOMParseErrorPtr pParseError = spXMLDOMDocument->GetparseError(); strstream strErr; strErr << hex << pParseError->GeterrorCode() << ends; static string strReason(strErr.str()); strReason += _T(": "); strReason += pParseError->Getreason(); // Remove goofy final characters. strReason = strReason.substr(0, strReason.length() - 2); return strReason.data(); } // Spin The Message Loop: C++ version. See "Advanced Windows Programming", // M. Heller, p. 153, and the MS TechNet CD, PSS ID Number: Q99999. /* static */ UINT GenericAtl::SpinTheMessageLoop(bool bNoDrawing /* = false */ , bool bOnlyDrawing /* = false */ , UINT uiMsgAllowed /* = WM_NULL */ ) { MSG msg; ::ZeroMemory(&msg, sizeof(msg)); while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // Do painting only. if (bOnlyDrawing && WM_PAINT == msg.message) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } // Do everything *but* painting. else if (bNoDrawing && WM_PAINT == msg.message) { break; } // Special handling for this message. else if (WM_QUIT == msg.message) { ::PostQuitMessage(msg.wParam); break; } // Allow one message (like WM_LBUTTONDOWN). else if (uiMsgAllowed == msg.message /* && ! AfxGetApp()->PreTranslateMessage(&msg) */ ) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); break; } // This is the general case. else if (! bOnlyDrawing /* && ! AfxGetApp()->PreTranslateMessage(&msg) */ ) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } // Update user interface, then free temporary objects. //AfxGetApp()->OnIdle(0); //AfxGetApp()->OnIdle(1); return msg.message; } // Parse full path down to just dir. /* static */ string GenericAtl::DirFromPath(const string& strPath) { string strFile(strPath); int nIndex(strFile.find_last_of('\\')); if (-1 != nIndex) { strFile = strFile.substr(0, nIndex); } return strFile; } #if 0 ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// UNUSED MFC CODE KEPT FOR REFERENCE ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// //#include "MyRegistry.h" //#include //#include //#include // This corrects a problem with the ::GetDialogBaseUnits() call, which only // works for the system font (which no one ever uses in dialogs anymore). // Call from your view's OnSize() method, passing "this" as the first arg. // Note that the mult and div are done in separate steps deliberately, in // order to GUARANTEE the order of evaluation (parentheses DO NOT affect // the order of evaluation for operators of the same precedence level). /* static */ int GenericAtl::GetHorzPixels(CWnd* pWnd, int nDlgUnits) { ASSERT_VALID(pWnd); _ASSERTE(0 <= nDlgUnits); int lnReturnPixels(0); if (pWnd && ::IsWindow(pWnd->GetSafeHwnd())) { CRect r(0, 0, 4, 8); if (::MapDialogRect(pWnd->GetSafeHwnd(), r)) { lnReturnPixels = (nDlgUnits * r.right); lnReturnPixels /= 4L; } } return lnReturnPixels; } // This corrects a problem with the ::GetDialogBaseUnits() call, which only // works for the system font (which no one ever uses in dialogs anymore). // Call from your view's OnSize() method, passing "this" as the first arg. // Note that the mult and div are done in separate steps deliberately, in // order to GUARANTEE the order of evaluation (parenthese DO NOT affect // the order of evaluation for operators of the same precedence level). /* static */ int GenericAtl::GetVertPixels(CWnd* pWnd, int nDlgUnits) { ASSERT_VALID(pWnd); _ASSERTE(0 <= nDlgUnits); int lnReturnPixels(0); if (pWnd && ::IsWindow(pWnd->GetSafeHwnd())) { CRect r(0, 0, 4, 8); if (::MapDialogRect(pWnd->GetSafeHwnd(), r)) { lnReturnPixels = (nDlgUnits * r.bottom); lnReturnPixels /= 8L; } } return lnReturnPixels; } // Spin The Message Loop: C++ version. See "Advanced Windows Programming", // M. Heller, p. 153, and the MS TechNet CD, PSS ID Number: Q99999. /* static */ UINT GenericAtl::SpinTheMessageLoop(bool bNoDrawing /* = false */ , bool bOnlyDrawing /* = false */ , UINT uiMsgAllowed /* = WM_NULL */ ) { MSG msg; ::ZeroMemory(&msg, sizeof(msg)); while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // Do painting only. if (bOnlyDrawing && WM_PAINT == msg.message) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } // Do everything *but* painting. else if (bNoDrawing && WM_PAINT == msg.message) { break; } // Special handling for this message. else if (WM_QUIT == msg.message) { ::PostQuitMessage(msg.wParam); break; } // Allow one message (like WM_LBUTTONDOWN). else if (uiMsgAllowed == msg.message && ! AfxGetApp()->PreTranslateMessage(&msg)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); break; } // This is the general case. else if (! bOnlyDrawing && ! AfxGetApp()->PreTranslateMessage(&msg)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } } // Update user interface, then free temporary objects. AfxGetApp()->OnIdle(0); AfxGetApp()->OnIdle(1); return msg.message; } // Does the path in the Registry contain something like %windir% // that requires translation? /* static */ bool GenericAtl::TranslateEnvPath(CString& sPathRaw) { CString sPath; int nFirstPercent(0); bool bReturn(true); while (-1 != (nFirstPercent = sPathRaw.Find("%"))) { sPath += sPathRaw.Mid(0, nFirstPercent - 1); // Extract the %Mumble% sub-string. CString sSub(sPathRaw.Mid(nFirstPercent + 1)); int nSecondPercent(sSub.Find("%")); sSub = sSub.Mid(0, nSecondPercent); sPathRaw = sPathRaw.Mid(nSecondPercent + 2); // Translate the environment variable ("mumble"). CString sBuffer; DWORD dwRet(0UL); DWORD dwBufferSize(128); if (dwBufferSize > (dwRet = ::GetEnvironmentVariable(sSub, sBuffer.GetBuffer(dwBufferSize), dwBufferSize))) { ; } else { // Failed! bReturn = false; DOMYLOG ("::GetEnvironmentVariable failed: %d\n", dwRet); break; } sBuffer.ReleaseBuffer(); // Append the translated substring to the return path. sPath += sBuffer; } // Append whatever is left over and return. sPath += sPathRaw; sPathRaw = sPath; return bReturn; } // This handles the porblem of, how does a 32-bit program de-serialized a // "long double" that was serialized years ago by a 16-bit program. ("Long // double" in 16-bit is 80 buts; in 32-bit, its 64 bits! Go figure.) // // From Q129209: This moves the contents of the buffer into the floating point // register, which then then takes care of the automatic convertion // back to a 8-byte long double. /* static */ double GenericAtl::Load80BitLongDoubleTo64BitDouble(CArchive& ar) { double dReturn(0.0); BYTE byInteger[10]; ar.Read(byInteger, 10); _asm { fld TBYTE PTR byInteger; fstp dReturn; } return dReturn; } // This simply loads the string from the string table, but has two advantages. // First, it does error checking, which would normally never get done, but is // important in localized applications; and, it takes only one line of code in // the caller instead of two (with minimal overhead). /* static */ CString GenericAtl::LoadString(UINT uiId) { _ASSERTE(0U < uiId); CString s; // Note that if this fails, we no longer trust the string table. The user // will just have to buy and English dictionary. Note too that the caller // can determine if this function failed by noticing that the return string // begins with the string id, following by three colons, which should be a // pretty unusual legitimate string to be in the string table. if (! s.LoadString(uiId)) { s.Format("%d::: Error loading string from string table!", uiId); ::MessageBeep(MB_ICONEXCLAMATION); DOMYLOG (s + "\n"); _ASSERTE(! "Error loading string from string table!"); } return s; } // Access to data in ini file using a CString. Features include: check the // error conditions correctly, allow you to pass NULL's in, use CStrings // internally, doesn't use hardcoded numbers, handle default values, and allows // for an optional size to be passed. Returns FALSE to mean failure. /* static */ bool GenericAtl::GetFromIniFile(const CString& sIniFile, const CString& sSection, const CString& sEntry, const CString& sDefault, CString& sData, int nSize /* = MAX_PATH */ ) { _ASSERTE(0 < nSize && "Buffer size must be positive"); bool bReturn(false); if (0 < nSize) { sData.Empty(); DWORD dwReturn(::GetPrivateProfileString(sSection, sEntry, sDefault, sData.GetBuffer(nSize), nSize, sIniFile)); sData.ReleaseBuffer(); // Buffer was too small. if (nSize - 1L == static_cast (dwReturn) || nSize - 2L == static_cast (dwReturn)) { DOMYLOGST(API_FAILURE), "GetPrivateProfileString() buffer too small", dwReturn); } else if (sData.GetLength() != static_cast (dwReturn)) { // Some other error. DOMYLOGST(API_FAILURE), "GetPrivateProfileString() other error", dwReturn); } else { // All is well. bReturn = true; } } else { // Bad data passed in - length must be positive. DOMYLOGST(API_FAILURE), "Bad size", nSize); } return bReturn; } // Parse full path down to just title (the "8" part of "8.3"). /* static */ CString GenericAtl::TitleFromPath(const CString& sPath) { CString sFile(GenericAtl::FileFromPath(sPath)); int nIndex(sFile.ReverseFind('.')); if (-1 != nIndex) { sFile = sFile.Left(nIndex); } return sFile; } // Parse full path down to just filename. /* static */ CString GenericAtl::FileFromPath(const CString& sPath) { CString sFile(sPath); int nIndex(sFile.ReverseFind('\\')); if (-1 != nIndex) { sFile = sFile.Mid(nIndex + 1); } return sFile; } // Parse full path down to just dir. /* static */ CString GenericAtl::DirFromPath(const CString& sPath) { CString sFile(sPath); int nIndex(sFile.ReverseFind('\\')); if (-1 != nIndex) { sFile = sFile.Left(nIndex); } return sFile; } // Parse full path down to just filename and one dir. /* static */ CString GenericAtl::DirAndFileFromPath(const CString& sPath) { CString sFile(sPath); int nIndex(sFile.ReverseFind('\\')); if (-1 != nIndex) { CString sFile2(sFile.Left(nIndex)); nIndex = sFile2.ReverseFind('\\'); sFile = sFile.Mid(nIndex + 1); } return sFile; } // Parse full path down to just extension. /* static */ CString GenericAtl::ExtFromPath(const CString& sPath) { CString sExt(GenericAtl::FileFromPath(sPath)); int nIndex(sExt.ReverseFind('.')); if (-1 != nIndex) { sExt = sExt.Mid(nIndex + 1); } else { sExt.Empty(); } return sExt; } // Returns size in bytes, or zero to mean empty of non-existant. /* static */ _fsize_t GenericAtl::GetFileSize(const CString& sPath) { _finddata_t fi; ::ZeroMemory(&fi, sizeof(fi)); long lnHandle(_findfirst(sPath, &fi)); _ASSERTE(-1 != lnHandle && "Cannot find file on disk"); _ASSERTE(0 <= fi.size && "File size is negative"); return fi.size; } // What day number (1 thru 366) is the sent date? /* static */ int GenericAtl::GetJulianDayNumber(const CTime& tim) { return atoi(tim.Format("%j")); } // Leave before any further damage can be done to the registry. // Memory leaks are the least of our worries. /* static */ void GenericAtl::QuickExit(const CString& sFile, int nLine) { DOMYLOG ("QUICK_EXIT\n"); _ASSERTE(! "About to ::PostQuitMessage(-1)"); ::PostQuitMessage(-1); GenericAtl::SpinTheMessageLoop(); } // Format the system error number returned by ::GetLastError() to a string. /* static */ CString GenericAtl::GetErrorString() { CString s; // There is an error number still. if (0 != GetLastError()) { LPVOID lpMsgBuf = NULL; if (::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, // Flags. NULL, // Message source. GetLastError(), // Message id. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language. (LPTSTR) &lpMsgBuf, // Buffer to receive. 0, // Max size of buffer. NULL // Argument list. )) { // Remove goofy final characters. s = reinterpret_cast (lpMsgBuf); s = s.Left(s.GetLength() - 2); } else { DOMYLOG ("\n::FormatMessage() failed with error <%d>.\n", GetLastError()); } VERIFY(NULL == ::LocalFree(lpMsgBuf)); } else { // No error number. We must not return the default string, // "The operation completed successfully.", because that confuses the user. // Calling CString::LoadString() from here always fails with errno 1814: // "The specified resource name cannot be found in the image file." } return s; } // Given a number return a comma-ized string, like "1,234,567". /* static */ CString GenericAtl::IntToCommas(int nInt) { CString s; int nValue(abs(nInt)); while (nValue) { int nSegment(nValue % 1000); nValue /= 1000; CString sSegment; sSegment.Format(nValue ? "%03d," : "%d,", nSegment); s = sSegment + s; } // Remove trailing comma. s = s.Left(s.GetLength() - 1); // Add leading negative. if (0 > nInt) { s = "-" + s; } return s; } // Given a number return a comma-ized string, like "1,234,567.89". /* static */ CString GenericAtl::DoubleToCommas(double dDouble, int nDigitsAfterDecimal /* = 2 */ ) { // Handle rounding issue. bool bNegative(false); double dValue(GenericAtl::RoundDouble(dDouble, nDigitsAfterDecimal)); if (0.0 > dValue) { dValue = fabs(dValue); bNegative = true; } // Now do the stringizing. double dOrd(0.0); double dMan(modf(dValue, &dOrd)); CString sOrd(GenericAtl::IntToCommas(static_cast (dOrd))); CString sMan; sMan.Format("%.*f", nDigitsAfterDecimal, fabs(dMan)); // Build the return value; strip off leading zero from mantissa, // account for negative values. CString sValue(sOrd + sMan.Mid(1)); if (bNegative) { sValue = "-" + sValue; } return sValue; } // Given a string with commas, return an int. /* static */ int GenericAtl::CommasToInt(const CString& sCommas) { CString s(GenericAtl::StripNonNumeric(sCommas)); return atoi(s); } // Given a string with commas, return a double. /* static */ double GenericAtl::CommasToDouble(const CString& sCommas) { CString s(GenericAtl::StripNonNumeric(sCommas)); return atof(s); } // Given a string like "$ 123,567.89" remove everything except numerals and periods. /* static */ CString GenericAtl::StripNonNumeric(const CString& s) { CString sInput(s); CString sReturn; CString sPiece; while (! sInput.IsEmpty()) { sPiece = sInput.SpanIncluding("-.0123456789"); sReturn += sPiece; if (sPiece.GetLength() + 1 < sInput.GetLength()) { sInput = sInput.Mid(sPiece.GetLength() + 1); } else { break; } } return sReturn; } // Add half and truncate. /* static */ double GenericAtl::RoundDouble(double dDouble, int nDigitsAfterDecimal /* = 2 */ ) { double dRoundingAddition(5.0 / pow(10.0, nDigitsAfterDecimal + 1)); double dValue(dDouble + dRoundingAddition); dValue = (dValue * pow(10.0, nDigitsAfterDecimal)); dValue = floor(dValue) / pow(10.0, nDigitsAfterDecimal); return dValue; } ///////////////////////////////////////////////////////////////////////////// // Conversion routines: RGB to HLS (Red-Green-Blue to Hue-Luminosity-Saturation). // See Microsoft KnowledgeBase article Q29240. #define HLSMAX 240 // H,L, and S vary over 0-HLSMAX #define RGBMAX 255 // R,G, and B vary over 0-RGBMAX // HLSMAX BEST IF DIVISIBLE BY 6 // RGBMAX, HLSMAX must each fit in a byte (255). #define UNDEFINED (HLSMAX * 2 / 3) // Hue is undefined if Saturation is 0 // (grey-scale). This value determines // where the Hue scrollbar is initially // set for achromatic colors. // Convert RGB to HLS. /* static */ void GenericAtl::RGBtoHLS(COLORREF crRGB, WORD& wH, WORD& wL, WORD& wS) { // Get R, G, and B out of COLORREF. WORD wR(GetRValue(crRGB)); WORD wG(GetGValue(crRGB)); WORD wB(GetBValue(crRGB)); // Calculate "lightness". BYTE cMax(static_cast (max(max(wR, wG), wB))); BYTE cMin(static_cast (min(min(wR, wG), wB))); wL = (((cMax + cMin) * HLSMAX) + RGBMAX) / (2 * RGBMAX); // r=g=b --> achromatic case. if (cMax == cMin) { wS = 0; wH = UNDEFINED; } else { // Chromatic case. // Saturation. if (wL <= (HLSMAX / 2)) { wS = (((cMax - cMin) * HLSMAX) + ((cMax+cMin) / 2)) / (cMax + cMin); } else { wS = (((cMax - cMin) * HLSMAX) + ((2 * RGBMAX - cMax - cMin) / 2)) / (2 * RGBMAX - cMax - cMin); } // Hue. WORD wRdelta((((cMax - wR) * (HLSMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin)); WORD wGdelta((((cMax - wG) * (HLSMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin)); WORD wBdelta((((cMax - wB) * (HLSMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin)); if (wR == cMax) { wH = wBdelta - wGdelta; } else if (wG == cMax) { wH = (HLSMAX / 3) + wRdelta - wBdelta; } else { // B == cMax wH = ((2 * HLSMAX) / 3) + wGdelta - wRdelta; } if (wH < 0) { wH += HLSMAX; } if (wH > HLSMAX) { wH -= HLSMAX; } } } // Convert HLS to RGB. /* static */ COLORREF GenericAtl::HLStoRGB(WORD wH, WORD wL, WORD wS) { WORD wR(0); WORD wG(0); WORD wB(0); // Achromatic case. if (0 == wS) { wR = wG = wB = (wL * RGBMAX) / HLSMAX; if (UNDEFINED != wH) { _ASSERTE(! "ERROR"); } } else { // Chromatic case. WORD Magic1(0); WORD Magic2(0); // Set up magic numbers. if (wL <= HLSMAX / 2) { Magic2 = (wL * (HLSMAX + wS) + (HLSMAX / 2)) / HLSMAX; } else { Magic2 = wL + wS - ((wL * wS) + (HLSMAX / 2)) / HLSMAX; } Magic1 = 2 * wL - Magic2; // Get RGB, change units from HLSMAX to RGBMAX. wR = (HueToRGB(Magic1, Magic2, wH + (HLSMAX / 3)) * RGBMAX + (HLSMAX / 2)) / HLSMAX; wG = (HueToRGB(Magic1, Magic2, wH) * RGBMAX + (HLSMAX / 2)) / HLSMAX; wB = (HueToRGB(Magic1, Magic2, wH - (HLSMAX / 3)) * RGBMAX + (HLSMAX / 2)) / HLSMAX; } return RGB(wR,wG,wB); } // Utility routine for HLStoRGB. /* static */ WORD GenericAtl::HueToRGB(WORD w1, WORD w2, WORD wH) { // Range check: note values passed add/subtract thirds of range. if (wH < 0) { wH += HLSMAX; } if (wH > HLSMAX) { wH -= HLSMAX; } // Return r, g, or b value from this tridrant. if (wH < HLSMAX / 6) { return w1 + (((w2 - w1) * wH + (HLSMAX / 12)) / (HLSMAX / 6)); } if (wH < HLSMAX / 2) { return w2; } if (wH < (HLSMAX * 2) / 3) { return w1 + (((w2 - w1) * (((HLSMAX * 2) / 3) - wH) + (HLSMAX / 12)) / (HLSMAX / 6)); } else { return w1; } } // Appends Path2 to Path1. Neither path has to exist. /* static */ bool GenericAtl::AppendFiles(const CString& sPath1, const CString& sPath2) { _ASSERTE(! sPath1.IsEmpty()); _ASSERTE(! sPath2.IsEmpty()); bool bReturn(false); CFile file1; CFile file2; // Open the file objects. if (file1.Open(sPath1, CFile::modeCreate | CFile::modeNoTruncate | CFile::modeReadWrite | CFile::shareExclusive)) { if (file2.Open(sPath2, CFile::modeRead | CFile::shareDenyWrite)) { // Create a big buffer to transfer the data in. PBYTE pBuf = NULL; const int nBufSize(64 * 1024); try { pBuf = new BYTE[nBufSize]; } catch(CMemoryException* e) { DOMYLOGST (NEW_FAILURE), "BYTE"); pBuf = NULL; e->Delete(); } if (pBuf) { try { DWORD dwBytesRead(0); file1.SeekToEnd(); // Read in from file2, and append it to file1, until we hit eof // on file2. do { dwBytesRead = file2.Read(pBuf, nBufSize); file1.Write(pBuf, dwBytesRead); } while (nBufSize == dwBytesRead); // Done appending! Close the files. file1.Close(); file2.Close(); bReturn = true; DOMYLOG ("APPEND_FILE_SUCCESS %s and %s\n", sPath2, sPath1); } catch(CFileException* e) { DOMYLOGST (API_FAILURE), GenericAtl::GetErrorString(), ::GetLastError()); e->Delete(); } // Release memory. delete pBuf; pBuf = NULL; } } else { DOMYLOGST(API_FAILURE), "CFile::Open" + GenericAtl::GetErrorString(), ::GetLastError()); } } else { DOMYLOGST(API_FAILURE), "CFile::Open" + GenericAtl::GetErrorString(), ::GetLastError()); } return bReturn; } // Rename a file. /* static */ bool GenericAtl::RenameFile(const CString& sPath1, const CString& sPath2) { _ASSERTE(! sPath1.IsEmpty()); _ASSERTE(! sPath2.IsEmpty()); bool bReturn(false); try { CFile::Rename(sPath1, sPath2); bReturn = true; DOMYLOG ("Renamed %s to %s\n", sPath1, sPath2); } catch(CFileException* e) { DOMYLOGST(API_FAILURE), "CFile::Rename: " + GenericAtl::GetErrorString(), ::GetLastError()); e->Delete(); } return bReturn; } // Delete a file. /* static */ bool GenericAtl::RemoveFile(const CString& sPath) { _ASSERTE(! sPath.IsEmpty()); bool bReturn(false); try { CFile::Remove(sPath); bReturn = true; DOMYLOG ("Removed file %s\n", sPath); } catch(CFileException* e) { DOMYLOGST(API_FAILURE), "CFile::Remove " + GenericAtl::GetErrorString(), ::GetLastError()); e->Delete(); } return bReturn; } // Is the sent path a diectory? /* static */ bool GenericAtl::IsDirectory(const CString& sPath) { bool bReturn(false); struct _stat s; ::ZeroMemory(&s, sizeof s); // Does the path even exist? if (0 == _stat(sPath, &s)) { // Is it a dir? if (s.st_mode & _S_IFDIR) { bReturn = true; } } return bReturn; } // Converts from pointsize to logical units. /* static */ int GenericAtl::PointsToLogicalHeight(const CDC* pDC, int nPoints) { ASSERT_VALID(pDC); _ASSERTE(0 < nPoints); if (pDC) { return -MulDiv(nPoints, pDC->GetDeviceCaps(LOGPIXELSY), /* nPointsPerInch */ 72); } else { _ASSERTE(! "pDC is NULL"); return -1; } } // Searches for the last match of a substring. /* static */ int GenericAtl::ReverseFindOneOf(const CString& s, const CString& sSub, int nStart /* = -1 */ ) { int nReturn(-1); int nLen(s.GetLength()); int nLenSub(sSub.GetLength()); if (0 < nLenSub && 0 < nLen) { if (-1 == nStart || nStart >= nLen) { nStart = nLen - 1; } for (int nX = nStart; nX >= 0; --nX) { if (0 == s.Mid(nX,1).FindOneOf(sSub)) { nReturn = nX; break; } } } return nReturn; } // /* static */ bool GenericAtl::RemoveTrailingSlash(CString& sPath) { bool bReturn(false); if (! sPath.IsEmpty()) { CString sLast(sPath.Right(1)); if (0 == sLast.Compare("\\")) { int nLen(sPath.GetLength()); sPath = sPath.Left(nLen - 1); bReturn = true; } } return bReturn; } // Pass in the following: // // sPlacementKey - // "Software\Sales Technologies\Dr Scrub 3\ScrubConfig\Placements" // // sPlacementSubKey - // "ScrubRequestDetailsDlg" // // We will use the Values of 'WindowPlacement' and 'LastScreenWidthPixels'. /* static */ void GenericAtl::SaveWindowPlacement(const CWnd* pWnd, const CString& sPlacementKey, const CString& sPlacementSubKey) { ASSERT_VALID(pWnd); _ASSERTE(! sPlacementKey.IsEmpty()); _ASSERTE(! sPlacementSubKey.IsEmpty()); int nLastScreenWidthPixels(0); // Save the position for the next time. MyRegistry regWrite(HKEY_CURRENT_USER, sPlacementKey + "\\" + sPlacementSubKey, /* bCreate */ true); VERIFY(regWrite.IsOpen()); if (regWrite.IsOpen()) { WINDOWPLACEMENT wp; ::ZeroMemory(&wp, sizeof(wp)); wp.length = sizeof(wp); VERIFY(pWnd->GetWindowPlacement(&wp)); VERIFY(regWrite.SaveValueBinary("WindowPlacement", reinterpret_cast (&wp), sizeof(wp))); // Save the screen resolution, so that when we re-start we can detect if // it's changed. If so, we won't size and position, because it causes // problems. nLastScreenWidthPixels = ::GetSystemMetrics(SM_CXSCREEN); VERIFY(regWrite.SaveValueInt("LastScreenWidthPixels", nLastScreenWidthPixels)); } } // Pass in the following: // // sPlacementKey - // "Software\Sales Technologies\Dr Scrub 3\ScrubConfig\Placements" // // sPlacementSubKey - // "ScrubRequestDetailsDlg" // // We will use the Values of 'WindowPlacement' and 'LastScreenWidthPixels'. /* static */ void GenericAtl::RestoreWindowPlacement( CWnd* pWnd, const CString& sPlacementKey, const CString& sPlacementSubKey, int nShowWindowCmd /* = -1 */ ) { ASSERT_VALID(pWnd); _ASSERTE(! sPlacementKey.IsEmpty()); _ASSERTE(! sPlacementSubKey.IsEmpty()); // Restore the previous run's position (but only if it exists in the // Registry, and the screen reolution hasn't changed). MyRegistry reg(HKEY_CURRENT_USER, sPlacementKey + "\\" + sPlacementSubKey, /* bCreate */ false); if (reg.IsOpen()) { // Compare the screen resolution when the placement values were saved to // the screen resolution in use now. int nCurrentScreenWidthPixels(::GetSystemMetrics(SM_CXSCREEN)); int nLastScreenWidthPixels(0); bool bFoundLast(reg.ReadValueInt("LastScreenWidthPixels", nLastScreenWidthPixels)); if (! bFoundLast || nCurrentScreenWidthPixels == nLastScreenWidthPixels) { WINDOWPLACEMENT wp; ::ZeroMemory(&wp, sizeof(wp)); wp.length = sizeof(wp); if (reg.ReadValueBinary("WindowPlacement", reinterpret_cast (&wp), reinterpret_cast (wp.length))) { // If the user wants to override what was found, do so. if (-1 != nShowWindowCmd) { wp.showCmd = nShowWindowCmd; } // Place the window. VERIFY(pWnd->SetWindowPlacement(&wp)); } } else { // The screen resolution changed since the last time we ran, or they // were deleted by the OnDestroy() handler. Don't load those obsolete // or non-existent placement values. } } } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// END OF UNUSED MFC CODE ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// #endif