#include "stdafx.h" #include "category.h" #include "MSInfo5Category.h" #include "FileIO.h" #include "filestuff.h" #include #include "fdi.h" #include "cabfunc.h" #include "dataset.h" #include "resource.h" #include "msxml.h" #include "wbemcli.h" /////////////// //CMSInfo7Category CMSInfo7Category::CMSInfo7Category() { this->m_iColCount = 0; this->m_iRowCount = 0; this->m_pFirstChild = NULL; this->m_pNextSibling = NULL; this->m_pParent = NULL; this->m_pPrevSibling = NULL; } CMSInfo7Category::~CMSInfo7Category() { this->DeleteAllContent(); } //----------------------------------------------------------------------------- // Static member of CMSInfo7Category // Starts the process of reading a file, creating a new CMSInfo7Category object // for each category found, and returning a pointer to the root node //----------------------------------------------------------------------------- HRESULT CMSInfo7Category::ReadMSI7NFO(CMSInfo7Category** ppRootCat, LPCTSTR szFilename) { HRESULT hr = E_FAIL; CMSInfo7Category* pRootCat = new CMSInfo7Category(); do { if (!pRootCat) break; if (!pRootCat->LoadFromXML(szFilename)) { delete pRootCat; pRootCat = NULL; break; } if (szFilename) { CString strAppend; strAppend.Format(_T(" (%s)"), szFilename); pRootCat->m_strCaption += strAppend; } hr = S_OK; } while (false); *ppRootCat = pRootCat; return hr; } BOOL CMSInfo7Category::LoadFromXML(LPCTSTR szFilename) { CComPtr pDoc; CComPtr pNode; HRESULT hr; VARIANT_BOOL vb; BOOL retVal = FALSE; CoInitialize(NULL); do { hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&pDoc); if (FAILED(hr) || !pDoc) break; pDoc->put_async(VARIANT_FALSE); hr = pDoc->load(CComVariant(szFilename), &vb); if (FAILED(hr) || !vb) break; hr = pDoc->QueryInterface(IID_IXMLDOMNode, (void**)&pNode); if (FAILED(hr) || !pNode) break; if (FAILED(WalkTree(pNode, FALSE))) break; retVal = TRUE; }while (false); CoUninitialize(); return retVal; } HRESULT CMSInfo7Category::WalkTree(IXMLDOMNode* node, BOOL bCreateCategory) { CComPtr childList, rowList; IXMLDOMNodeList* columnList = NULL; CComPtr attributeMap; IXMLDOMNode* pNextChild = NULL, *pNextAttribute = NULL, *pNextColumn = NULL; //CComBSTR xmlStr; //node->get_xml(&xmlStr); //read attributes, data & recurse for child categories. //attributes if (SUCCEEDED(node->get_attributes(&attributeMap)) && attributeMap != NULL) { attributeMap->nextNode(&pNextAttribute); while (pNextAttribute) { CComBSTR bstrName, bstrValue; pNextAttribute->get_nodeName(&bstrName); pNextAttribute->get_text(&bstrValue); if (lstrcmpiW(bstrName, L"Name") == 0) { m_strName = bstrValue; m_strCaption = m_strName; pNextAttribute->Release(); break; } pNextAttribute->Release(); attributeMap->nextNode(&pNextAttribute); } } m_iColCount = 0; m_iRowCount = 0; //count number of rows if (SUCCEEDED(node->selectNodes(CComBSTR("Data"), &rowList)) && rowList != NULL) { long len = 0; rowList->get_length(&len); m_iRowCount = len; } if (m_iRowCount > 0) m_afRowAdvanced = new BOOL[m_iRowCount]; int iRow = 0; CMSInfo7Category* pPrevCat = NULL; //children if (SUCCEEDED(node->get_childNodes(&childList)) && childList != NULL) { childList->nextNode(&pNextChild); while (pNextChild) { CComBSTR bstrName; pNextChild->get_nodeName(&bstrName); if ((lstrcmpiW(bstrName, L"Data") == 0) && (m_iRowCount > 0)) { //for each data(row), read column name & value if (SUCCEEDED(pNextChild->get_childNodes(&columnList)) && columnList != NULL) { BOOL bColumnsInitialized = m_acolumns ? TRUE : FALSE; if (!bColumnsInitialized)//column static data. do once { long len = 0; columnList->get_length(&len); m_iColCount = len; m_fDynamicColumns = TRUE;//for correct deletion. if (m_iColCount > 0) m_acolumns = new CMSInfoColumn[m_iColCount]; else m_acolumns = NULL; if (m_iColCount > 0 && m_iRowCount > 0) { m_astrData = new CString[m_iColCount * m_iRowCount]; m_adwData = new DWORD[m_iColCount * m_iRowCount]; } else { m_astrData = NULL; m_adwData = NULL; } } int iColumn = 0; columnList->nextNode(&pNextColumn); while (pNextColumn) { CComBSTR bstrColHdr, bstrRowVal; if (!bColumnsInitialized)//column static data. do once { pNextColumn->get_nodeName(&bstrColHdr);//column hdr m_acolumns[iColumn].m_strCaption = bstrColHdr; m_acolumns[iColumn].m_uiWidth = 150;//PENDING m_acolumns[iColumn].m_fSorts = FALSE;//PENDING m_acolumns[iColumn].m_fLexical = FALSE;//PENDING m_acolumns[iColumn].m_fAdvanced = FALSE;//PENDING m_acolumns[iColumn].m_uiCaption = 0; } pNextColumn->get_text(&bstrRowVal);//row value for the column if(lstrcmpiW(bstrColHdr, L"MSINFOERROR") == 0) m_hrError = _ttoi(bstrRowVal); m_astrData[iRow * m_iColCount + iColumn] = bstrRowVal; m_afRowAdvanced[iRow] = FALSE;//PENDING if (m_acolumns[iColumn].m_fSorts && !m_acolumns[iColumn].m_fLexical) { //m_adwData[iRow * m_iColCount + iColumn] = uiSortOrder;//PENDING } iColumn++; pNextColumn->Release(); columnList->nextNode(&pNextColumn); } } if (columnList) columnList->Release(); iRow++; } else if ((lstrcmpiW(bstrName, L"XML") == 0) || (lstrcmpiW(bstrName, L"MSInfo") == 0)) { //get past & this->WalkTree(pNextChild, bCreateCategory); } else if (lstrcmpiW(bstrName, L"Category") == 0) { if (!bCreateCategory) { bCreateCategory = TRUE;//First category encountered. Subsequent categories get their own node. this->WalkTree(pNextChild, bCreateCategory); } else { CMSInfo7Category* pNewCat = new CMSInfo7Category(); pNewCat->SetParent(this); pNewCat->SetPrevSibling(pPrevCat); if (pPrevCat) pPrevCat->SetNextSibling(pNewCat); else m_pFirstChild = pNewCat; pPrevCat = pNewCat; pNewCat->WalkTree(pNextChild, bCreateCategory); } } pNextChild->Release(); childList->nextNode(&pNextChild); } } return S_OK; } // we want these msgs to look very similar to those displayed by the live category void CMSInfo7Category::GetErrorText(CString * pstrTitle, CString * pstrMessage) { if (SUCCEEDED(m_hrError)) { ASSERT(0 && "why call GetErrorText for no error?"); CMSInfoCategory::GetErrorText(pstrTitle, pstrMessage); return; } if (pstrTitle) pstrTitle->LoadString(IDS_CANTCOLLECT); if (pstrMessage) { switch (m_hrError) { case WBEM_E_OUT_OF_MEMORY: pstrMessage->LoadString(IDS_OUTOFMEMERROR); break; case WBEM_E_ACCESS_DENIED: pstrMessage->LoadString(IDS_GATHERACCESS_LOCAL); break; case WBEM_E_INVALID_NAMESPACE: pstrMessage->LoadString(IDS_BADSERVER_LOCAL); break; case 0x800706BA: // RPC Server Unavailable case WBEM_E_TRANSPORT_FAILURE: pstrMessage->LoadString(IDS_NETWORKERROR_LOCAL); break; case WBEM_E_FAILED: case WBEM_E_INVALID_PARAMETER: default: pstrMessage->LoadString(IDS_UNEXPECTED); } #ifdef _DEBUG { CString strTemp; strTemp.Format(_T("\n\r\n\rDebug Version Only: [HRESULT = 0x%08X]"), m_hrError); *pstrMessage += strTemp; } #endif } } /////////////// //EO CMSInfo7Category CMSInfo5Category::CMSInfo5Category() { this->m_pFirstChild = NULL; this->m_pNextSibling = NULL; this->m_pParent = NULL; this->m_pPrevSibling = NULL; } //----------------------------------------------------------------------------- // Saves the actual data (by row and column) to the file // //----------------------------------------------------------------------------- void CMSInfoCategory::SaveElements(CMSInfoFile *pFile) { CString szWriteString; MSIColumnSortType stColumn; unsigned iColCount; unsigned iRowCount; GetCategoryDimensions((int*) &iColCount,(int*) &iRowCount); DataComplexity dcAdvanced; CArray aColumnValues; pFile->WriteUnsignedInt(iColCount); if (iColCount == 0) return; //for(unsigned iCol = 0; iCol < iColCount; iCol++) for(int iCol = iColCount - 1; iCol >= 0 ; iCol--) { unsigned uWidth; BOOL bSort,bLexical; GetColumnInfo(iCol,&szWriteString,&uWidth,&bSort,&bLexical); if (bSort) { if (bLexical) { stColumn = LEXICAL; } else { stColumn = BYVALUE; } } else { stColumn = NOSORT; } if (IsColumnAdvanced(iCol)) { dcAdvanced = ADVANCED; } else { dcAdvanced = BASIC; } if (stColumn == BYVALUE) { aColumnValues.Add(iCol); } pFile->WriteUnsignedInt(uWidth); pFile->WriteString(szWriteString); pFile->WriteUnsignedInt((unsigned) stColumn); pFile->WriteByte((BYTE)dcAdvanced); } int wNextColumn = -1; unsigned iArray = 0; pFile->WriteUnsignedInt(iRowCount); //for(int iRow = 0; iRow < (int) iRowCount; iRow++) for(int iRow = iRowCount - 1; iRow >= 0 ; iRow--) { if (IsRowAdvanced(iRow)) { dcAdvanced = ADVANCED; } else { dcAdvanced = BASIC; } pFile->WriteByte((BYTE)dcAdvanced); } // Iterate over columns, writing sort indices for BYVALUE columns. DWORD dwSortIndex; //for(iCol = 0; iCol < iColCount; iCol++) for(iCol = iColCount - 1; iCol >= 0 ; iCol--) { //following variables are not used except for sort info CString strUnused; UINT iWidth; BOOL fSorts; BOOL fLexical; GetColumnInfo(iCol,&strUnused,&iWidth,&fSorts,&fLexical); CDWordArray arySortIndices; //for(unsigned iRow = 0; iRow < iRowCount; iRow++) for(int iRow = iRowCount - 1; iRow >= 0 ; iRow--) { CString * pstrData; this->GetData(iRow, iCol, &pstrData, &dwSortIndex); // dwSortIndex = m_adwData[iRow * m_iColCount + iCol]; if (fSorts && !fLexical) { arySortIndices.Add(dwSortIndex); } // szWriteString = m_astrData[iRow * m_iColCount + iCol]; pFile->WriteString(*pstrData); } if (fSorts && !fLexical) { ASSERT((unsigned) arySortIndices.GetSize() == iRowCount && "wrong number of Sort indices"); //for(unsigned iRow = 0; iRow < iRowCount; iRow++) for(int iRow = iRowCount - 1; iRow >= 0 ; iRow--) { pFile->WriteUnsignedLong(arySortIndices.GetAt(iRow)); } } } } //----------------------------------------------------------------------------- // Fills the various data structures with information in a msinfo file //----------------------------------------------------------------------------- BOOL CMSInfo5Category::LoadFromNFO(CMSInfoFile* pFile) { //TD: check validity of the file try { pFile->ReadString(this->m_strName); this->m_strCaption = this->m_strName; pFile->ReadSignedInt(this->m_iColCount); if (m_iColCount == 0) { this->m_iRowCount = 0; return TRUE; } this->m_acolumns = new CMSInfoColumn[m_iColCount]; for(int iColumn = m_iColCount - 1; iColumn >= 0; iColumn--) { UINT uiWidth; pFile->ReadUnsignedInt(uiWidth); CString strCaption; pFile->ReadString(strCaption); unsigned wSortType; pFile->ReadUnsignedInt(wSortType); BOOL fSorts; BOOL fLexical; if ( NOSORT == wSortType) { fLexical = FALSE; fSorts = FALSE; } else if (BYVALUE == wSortType) { fLexical = FALSE; fSorts = TRUE; } else { fLexical = TRUE; fSorts = TRUE; } BOOL fAdvanced; BYTE btAdvanced; pFile->ReadByte(btAdvanced); fAdvanced = (BOOL) btAdvanced; m_acolumns[iColumn].m_strCaption = strCaption; m_acolumns[iColumn].m_uiWidth = uiWidth; m_acolumns[iColumn].m_fSorts = fSorts; m_acolumns[iColumn].m_fLexical = fLexical; m_acolumns[iColumn].m_fAdvanced = fAdvanced; m_acolumns[iColumn].m_uiCaption = 0; } pFile->ReadSignedInt(this->m_iRowCount); //Nodes that have no data, but only serve as parents to other nodes, have no Rows //and a column count of 1 m_astrData = new CString[m_iColCount * m_iRowCount]; m_adwData = new DWORD[m_iColCount * m_iRowCount]; m_afRowAdvanced = new BOOL[m_iRowCount]; //for(int iRow = 0; iRow < m_iRowCount; iRow++) for(int iRow = m_iRowCount - 1; iRow >=0; iRow--) { BYTE bComplexity; pFile->ReadByte(bComplexity); if (BASIC == bComplexity) { this->m_afRowAdvanced[iRow] = FALSE; } else { this->m_afRowAdvanced[iRow] = TRUE; } } for(iColumn = m_iColCount - 1; iColumn >= 0; iColumn--) { CMSInfoColumn* pCol = &this->m_acolumns[(unsigned)iColumn]; //for(iRow = 0; iRow < this->m_iRowCount; iRow++) for(int iRow = m_iRowCount - 1; iRow >=0; iRow--) { CString strData; pFile->ReadString(strData); m_astrData[iRow * m_iColCount + iColumn] = strData; } //sort values are another row of ints like Complexity //for(iRow = 0; iRow < this->m_iRowCount; iRow++) for(iRow = m_iRowCount - 1; iRow >=0; iRow--) { CMSInfoColumn* pColInfo = &this->m_acolumns[iColumn]; if (pColInfo->m_fSorts && !pColInfo->m_fLexical) { unsigned uiSortOrder; pFile->ReadUnsignedInt(uiSortOrder); m_adwData[iRow * m_iColCount + iColumn] = uiSortOrder; } } } } //TD: exception handling catch (CFileException* pException) { pException->ReportError(); pException->Delete(); return FALSE; } catch (CFileFormatException* pException) { pException->Delete(); return FALSE; } catch (...) { ::AfxSetResourceHandle(_Module.GetResourceInstance()); //messaging is actually handled elsewhere /*CString strCaption, strMessage; strCaption.LoadString(IDS_SYSTEMINFO); strMessage.LoadString(IDS_BADNFOFILE); ::MessageBox(NULL,strMessage, strCaption,MB_OK);*/ return FALSE; } return TRUE; } //----------------------------------------------------------------------------- // Read the header information found at the beginning of the file //----------------------------------------------------------------------------- BOOL ReadMSI5NFOHeader(CMSInfoFile* pFile) { unsigned iMsinfoFileVersion; try { pFile->ReadUnsignedInt(iMsinfoFileVersion); if (iMsinfoFileVersion == CMSInfoFile::VERSION_500_MAGIC_NUMBER) { unsigned uVersion; pFile->ReadUnsignedInt(uVersion); ASSERT(uVersion == 0x500 && "Version number does not match format #"); if (uVersion != 0x500) { return NULL; } } else { return NULL; } LONG l; pFile->ReadLong(l); // Save time. time_t tsSaveTime = (ULONG) l; //TD: sanity test on date CString szUnused; pFile->ReadString(szUnused); // Network machine name pFile->ReadString(szUnused); // Network user name } catch (CFileException* pException) { pException->ReportError(); pException->Delete(); return FALSE; } catch (CFileFormatException* pException) { //TD: exception handling pException->Delete(); return FALSE; } catch (...) { //messagebox the user in OpenMSInfoFile return FALSE; } return TRUE; } CMSInfo5Category::~CMSInfo5Category() { this->DeleteAllContent(); }; //----------------------------------------------------------------------------- // Static member of CMSInfo5Category // Starts the process of reading a file, creating a new CMSInfo5Category object // for each category found, and returning a pointer to the root node //----------------------------------------------------------------------------- HRESULT CMSInfo5Category::ReadMSI5NFO(HANDLE hFile,CMSInfo5Category** ppRootCat, LPCTSTR szFilename) { CMSInfo5Category* pRootCat = new CMSInfo5Category(); CFile* pFile = new CFile((INT_PTR) hFile); CMSInfoFile msiFile(pFile); unsigned iNodeData; if (!ReadMSI5NFOHeader(&msiFile)) { //make sure this gets in 2/14 checkin! /* CString strCaption, strMessage; ::AfxSetResourceHandle(_Module.GetResourceInstance()); strCaption.LoadString(IDS_SYSTEMINFO); strMessage.LoadString(IDS_BADNFOFILE); MessageBox(NULL,strMessage, strCaption,MB_OK);*/ return E_FAIL; } try { CMSInfo5Category* pCat = NULL; CMSInfo5Category* pPreviousCat; if (!pRootCat->LoadFromNFO(&msiFile)) { delete pRootCat; pRootCat = NULL; CString strCaption, strMessage; ::AfxSetResourceHandle(_Module.GetResourceInstance()); strCaption.LoadString(IDS_SYSTEMINFO); strMessage.LoadString(IDS_BADNFOFILE); MessageBox(NULL,strMessage, strCaption,MB_OK); return E_FAIL; } //there will be a dummy System Information node, with colcount of 1 and rowcount of 0 //we need to discard this. if (pRootCat->m_iColCount == 1 && pRootCat->m_iRowCount == 0) { delete pRootCat; pRootCat = new CMSInfo5Category(); msiFile.ReadUnsignedInt(iNodeData); if (!pRootCat->LoadFromNFO(&msiFile)) { delete pRootCat; pRootCat = NULL; CString strCaption, strMessage; ::AfxSetResourceHandle(_Module.GetResourceInstance()); strCaption.LoadString(IDS_SYSTEMINFO); strMessage.LoadString(IDS_BADNFOFILE); MessageBox(NULL,strMessage, strCaption,MB_OK); return E_FAIL; } } if (szFilename) { CString strAppend; strAppend.Format(_T(" (%s)"), szFilename); pRootCat->m_strCaption += strAppend; } pPreviousCat = pRootCat; unsigned iNextNodeType = CMSInfo5Category::FIRST; //iNextNodeType specifies where in the node tree to put the category for(;iNextNodeType != CMSInfo5Category::END;) { msiFile.ReadUnsignedInt(iNodeData); if (pPreviousCat == pRootCat) { //disregard this particular node position indicator, since we //don't want an empty root category like MSInfo 5.0 iNodeData = CMSInfo5Category::CHILD; } iNextNodeType = iNodeData & CMSInfo5Category::MASK; switch (iNextNodeType) { case CMSInfo5Category::END: pPreviousCat->SetNextSibling(NULL); pPreviousCat->SetFirstChild(NULL); break; case CMSInfo5Category::NEXT: pCat = new CMSInfo5Category(); if (!pCat->LoadFromNFO(&msiFile)) { delete pCat; pCat = NULL; return E_FAIL; } pCat->SetPrevSibling(pPreviousCat); //the parent of the previous sibling should be the parent for this if (pPreviousCat) { pCat->SetParent((CMSInfo5Category *) pPreviousCat->GetParent()); pPreviousCat->SetNextSibling(pCat); pCat->SetPrevSibling(pPreviousCat); } pPreviousCat = pCat; break; case CMSInfo5Category::CHILD: pCat = new CMSInfo5Category(); if (!pCat->LoadFromNFO(&msiFile)) { delete pCat; pCat = NULL; return E_FAIL; } pCat->SetParent(pPreviousCat); pPreviousCat->SetFirstChild(pCat); pCat->SetPrevSibling(NULL); pPreviousCat = pCat; break; case CMSInfo5Category::PARENT: pCat = new CMSInfo5Category(); if (!pCat->LoadFromNFO(&msiFile)) { delete pCat; pCat = NULL; return E_FAIL; } //if this a parent, we need to backtrack out of current branch of tree //to find the appropriate parent, get an index from iNodeData //and go back that many categories. unsigned iDepth = (iNodeData & ~CMSInfo5Category::MASK); for(unsigned i = 0; i < iDepth; i++) { pPreviousCat = (CMSInfo5Category *) pPreviousCat->GetParent(); } if (!pPreviousCat) { return E_FAIL; } //now move to the end of chain of children for(;pPreviousCat->GetNextSibling();) { pPreviousCat = (CMSInfo5Category *) pPreviousCat->GetNextSibling(); } pPreviousCat->SetNextSibling(pCat); pCat->SetParent((CMSInfo5Category *) pPreviousCat->GetParent()); pCat->SetPrevSibling(pPreviousCat); pCat->SetNextSibling(NULL); pPreviousCat = pCat; break; } } } catch (CFileException* pException) { pException->ReportError(); pException->Delete(); return E_FAIL; } catch (CFileFormatException* pException) { //TD: cleanup pException->Delete(); return E_FAIL; } catch (...) { ::AfxSetResourceHandle(_Module.GetResourceInstance()); CString strCaption, strMessage; strCaption.LoadString(IDS_SYSTEMINFO); strMessage.LoadString(IDS_BADNFOFILE); ::MessageBox(NULL,strMessage, strCaption,MB_OK); return E_FAIL; } //no need to delete pFile; it will be cleaned up by CMSInfoFile destructor *ppRootCat = pRootCat; return S_OK; } //----------------------------------------------------------------------------- // Saves this category to a MSInfo 5 file, which must already have header information // written to it //----------------------------------------------------------------------------- BOOL CMSInfoCategory::SaveToNFO(CMSInfoFile* pFile) { CString strCaption; GetNames(&strCaption, NULL); pFile->WriteString(strCaption); SaveElements(pFile); return TRUE; } HANDLE CMSInfo5Category::GetFileFromCab(CString strFileName) { CString strDest; GetCABExplodeDir(strDest,TRUE,""); OpenCABFile(strFileName,strDest); CString strFilename; FindFileToOpen(strDest,strFilename); return CreateFile(strFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); } //----------------------------------------------------------------------------- //Saves category information as text, recursing children in bRecursive is true //----------------------------------------------------------------------------- BOOL CMSInfoCategory::SaveAsText(CMSInfoTextFile* pTxtFile, BOOL bRecursive) { CString strOut; CString strBracket; VERIFY(strBracket.LoadString(IDS_LEFTBRACKET) && "Failed to find resource IDS_LEFTBRACKET"); strOut = strBracket; CString strName, strCaption; GetNames(&strCaption,&strName); strOut += strCaption; VERIFY(strBracket.LoadString(IDS_RIGHTBRACKET) && "Failed to find resource IDS_RIGHTBRACKET"); strOut += strBracket; pTxtFile->WriteString("\r\n"); pTxtFile->WriteString(strOut); int iRowCount,iColCount; this->GetCategoryDimensions(&iColCount,&iRowCount); CString strColHeader; UINT uiUnused; BOOL fUnused; CString strColSpacing = "\t"; pTxtFile->WriteString("\r\n"); pTxtFile->WriteString("\r\n"); if (1 == iColCount && 0 == iRowCount) { //this is a parent node, with no data of its own CString strCatHeading; strCatHeading.LoadString(IDS_CATEGORYHEADING); pTxtFile->WriteString(strCatHeading); } else { //for(int iCol = iColCount - 1; iCol >= 0 ; iCol--) for(int iCol = 0; iCol < iColCount ; iCol++) { GetColumnInfo(iCol,&strColHeader,&uiUnused,&fUnused,&fUnused); pTxtFile->WriteString(strColHeader); pTxtFile->WriteString(strColSpacing); } pTxtFile->WriteString("\r\n"); CString strRowInfo; //for(int iRow = iRowCount - 1; iRow >= 0; iRow--) for(int iRow = 0;iRow < iRowCount; iRow++) { //for(int iCol = iColCount - 1; iCol >= 0 ; iCol--) for(int iCol = 0; iCol < iColCount ; iCol++) { //this->GetData(iRow,iCol,&strRowInfo,&dwUnused); if(m_astrData) strRowInfo = m_astrData[iRow * m_iColCount + iCol]; else strRowInfo.LoadString(IDS_CANTCOLLECT); pTxtFile->WriteString(strRowInfo); pTxtFile->WriteString(strColSpacing); } pTxtFile->WriteString("\r\n"); } } if (bRecursive && this->m_pFirstChild != NULL) { for(CMSInfo5Category* pChild = (CMSInfo5Category*) this->GetFirstChild();pChild != NULL;pChild = (CMSInfo5Category*) pChild->GetNextSibling()) { pChild->SaveAsText(pTxtFile,TRUE); } } return TRUE; } /////////////////////// //Functions added by a-kjaw // The code needs to be refined & is copy-paste from SaveAsText code. //Maybe a good idea to parameterize SaveAsText func. /*BOOL CMSInfoCategory::SaveAsXml(CMSInfoTextFile* pTxtFile, BOOL bRecursive) { //if this has no parent, it is topmost node, so //if we're writing whole tree (bRecursive is TRUE) then now is when //we want to write header if (!this->m_pParent && bRecursive) { pTxtFile->WriteString("\r\n"); pTxtFile->WriteString("\r\n"); } CStringArray csarr; CString strOut; CString strBracket; CString strName, strCaption; GetNames(&strCaption,&strName); strOut += strCaption; strOut += strBracket; pTxtFile->WriteString("WriteString(strOut); pTxtFile->WriteString("\">\r\n"); int iRowCount,iColCount; this->GetCategoryDimensions(&iColCount,&iRowCount); CString strColHeader; UINT uiUnused; BOOL fUnused; int iSpaceLoc = 0; if (1 == iColCount && 0 == iRowCount) { //this is a parent node, with no data of its own //CString strCatHeading; //strCatHeading.LoadString(IDS_CATEGORYHEADING); //pTxtFile->WriteString(""); //pTxtFile->WriteString(strCatHeading); //pTxtFile->WriteString("\r\n"); } else { //for(int iCol = iColCount - 1; iCol >= 0 ; iCol--) csarr.RemoveAll(); for(int iCol = 0; iCol < iColCount ; iCol++) { //XML wont accept spaces at node names. ie. should be GetColumnInfo(iCol,&strColHeader,&uiUnused,&fUnused,&fUnused); while((iSpaceLoc = strColHeader.Find(_T(" ") , 0)) != -1) strColHeader.SetAt(iSpaceLoc , _T('_')); csarr.Add(strColHeader); } CString strRowInfo; for(int iRow = 0;iRow < iRowCount; iRow++) { for(int iCol = 0; iCol < iColCount ; iCol++) { strRowInfo = m_astrData[iRow * m_iColCount + iCol]; pTxtFile->WriteString("<"); pTxtFile->WriteString(csarr[iCol]); pTxtFile->WriteString(">"); //Put CDATA here to take care of all weird characters. pTxtFile->WriteString("WriteString(strRowInfo); pTxtFile->WriteString("]]>"); pTxtFile->WriteString("WriteString(csarr[iCol]); pTxtFile->WriteString(">\r\n"); } pTxtFile->WriteString("\r\n"); } pTxtFile->WriteString("\r\n"); } if (bRecursive && this->m_pFirstChild != NULL) { for(CMSInfo5Category* pChild = (CMSInfo5Category*) this->GetFirstChild();pChild != NULL;pChild = (CMSInfo5Category*) pChild->GetNextSibling()) { pChild->SaveAsXml(pTxtFile,TRUE); } } return TRUE; }*/ //----------------------------------------------------------------------------- // Saves this category as text to an open file, and recursively saves subcategories // if bRecursive is true // Assumes file can be close when last category is written //----------------------------------------------------------------------------- BOOL CMSInfoCategory::SaveAsText(HANDLE hFile, BOOL bRecursive, LPTSTR lpMachineName) { CFile * pFileOut = new CFile((INT_PTR)hFile); // The text file is Unicode, so it needs the marker (339423). WCHAR wUnicodeMarker = 0xFEFF; pFileOut->Write((const void *)&wUnicodeMarker, sizeof(WCHAR)); try { CMSInfoTextFile * pTxtFile = new CMSInfoTextFile(pFileOut); CTime tNow = CTime::GetCurrentTime(); CString strTimeFormat; VERIFY(strTimeFormat.LoadString(IDS_TIME_FORMAT) && "Failed to find resource IDS_TIME_FORMAT"); CString strHeaderText = tNow.Format(strTimeFormat); pTxtFile->WriteString(strHeaderText); if (NULL != lpMachineName) { CString strMachine; strMachine.LoadString(IDS_SYSTEMNAME); strMachine += _tcsupr(lpMachineName); pTxtFile->WriteString(strMachine); } if (!this->SaveAsText(pTxtFile,bRecursive)) { return FALSE; } delete pTxtFile; } catch(CFileException* pException) { pException->ReportError(); pException->Delete(); } catch (CException* pException) { pException->ReportError(); pException->Delete(); } catch(...) { ::AfxSetResourceHandle(_Module.GetResourceInstance()); CString strCaption, strMessage; strCaption.LoadString(IDS_SYSTEMINFO); strMessage.LoadString(IDS_FILESAVEERROR_UNKNOWN); ::MessageBox(NULL,strMessage, strCaption,MB_OK); } //CloseHandle(hFile); return TRUE; } /////////////////////// //Functions added by a-kjaw // The code needs to be refined & is copy-paste from SaveAsText code. //Maybe a good idea to parameterize SaveAsText func. /*BOOL CMSInfoCategory::SaveAsXml(HANDLE hFile, BOOL bRecursive) { CFile* pFileOut = new CFile((INT_PTR)hFile); try { CMSInfoTextFile* pTxtFile = new CMSInfoTextFile(pFileOut); if (!this->SaveAsXml(pTxtFile,bRecursive)) { return FALSE; } pTxtFile->WriteString("\r\n"); delete pTxtFile; } catch(CFileException e) { e.ReportError(); } catch (CException e) { e.ReportError(); } catch(...) { ::AfxSetResourceHandle(_Module.GetResourceInstance()); CString strCaption, strMessage; strCaption.LoadString(IDS_SYSTEMINFO); strMessage.LoadString(IDS_FILESAVEERROR_UNKNOWN); ::MessageBox(NULL,strMessage, strCaption,MB_OK); } //CloseHandle(hFile); return TRUE; }*/ //----------------------------------------------------------------------------- // Static function that saves specified category to a MSInfo 5 nfo file // writing header information (so it should be used only to save either root // category or single category //----------------------------------------------------------------------------- BOOL CMSInfoCategory::SaveNFO(HANDLE hFile, CMSInfoCategory* pCategory, BOOL fRecursive) { //msiFile will delete pFile in its destructor try { CFile* pFile = new CFile((INT_PTR) hFile); CMSInfoFile msiFile(pFile); msiFile.WriteHeader(NULL); if (!fRecursive || pCategory->GetParent() != NULL) { pCategory->SaveToNFO(&msiFile); msiFile.WriteEndMark(); return TRUE; } CMSInfoCategory* pNext = NULL; CMSInfoCategory* pRoot = pCategory; //change col and row count of pCategory, to use it as a to create empty node System Information //node, saving original col and row count int iRowCount, iColCount; iRowCount = pCategory->m_iRowCount; iColCount = pCategory->m_iColCount; pCategory->m_iColCount = 1; pCategory->m_iRowCount = 0; if (!pCategory->SaveToNFO(&msiFile)) { return FALSE; } //restore col and row counts pCategory->m_iColCount = iColCount; pCategory->m_iRowCount = iRowCount; //write child mark msiFile.WriteChildMark(); do { //write the data for each category as it is encountered if (!pCategory->SaveToNFO(&msiFile)) { return FALSE; } //if we have a child, traverse it pNext = pCategory->GetFirstChild(); if (pCategory == pRoot) { msiFile.WriteNextMark(); pCategory = pNext; continue; } else if (pNext != NULL) { msiFile.WriteChildMark(); pCategory = pNext; continue; } /*if (pCategory == pRoot) { break; }*/ //if we have reached the bottom of our list, traverse our siblings pNext = pCategory->GetNextSibling(); if (pNext != NULL) { msiFile.WriteNextMark(); pCategory = pNext; continue; } //if we have no more siblings, find our nearest parent's sibling, traversing //upwards until we find the node we started with pNext = pCategory->GetParent(); ASSERT(pNext != NULL); unsigned uParentCount = 0; while (pNext != pRoot) { ++uParentCount; pCategory = pNext->GetNextSibling(); //our parent has a sibling, continue with it if (pCategory != NULL) { msiFile.WriteParentMark(uParentCount); break; } pNext = pNext->GetParent(); } //if we've returned to our root node, we're done if (pNext == pRoot) { break; } } while (pCategory != NULL); msiFile.WriteEndMark(); } catch(CFileException* pException) { pException->ReportError(); pException->Delete(); } catch (CException* pException) { pException->ReportError(); pException->Delete(); } catch(...) { ::AfxSetResourceHandle(_Module.GetResourceInstance()); CString strCaption, strMessage; strCaption.LoadString(IDS_SYSTEMINFO); strMessage.LoadString(IDS_FILESAVEERROR_UNKNOWN); ::MessageBox(NULL,strMessage, strCaption,MB_OK); } return TRUE; } BOOL CMSInfoCategory::SaveXML(HANDLE hFile) { BOOL bRet = FALSE; CMSInfoTextFile* pTxtFile = NULL; CFile* pFileOut = new CFile((INT_PTR)hFile); try { pTxtFile = new CMSInfoTextFile(pFileOut); if (pTxtFile) bRet = SaveXML(pTxtFile); } catch(CFileException* pException) { pException->ReportError(); pException->Delete(); } catch (CException* pException) { pException->ReportError(); pException->Delete(); } catch(...) { ::AfxSetResourceHandle(_Module.GetResourceInstance()); CString strCaption, strMessage; strCaption.LoadString(IDS_SYSTEMINFO); strMessage.LoadString(IDS_FILESAVEERROR_UNKNOWN); ::MessageBox(NULL,strMessage, strCaption,MB_OK); } if (pTxtFile) { delete pTxtFile; pTxtFile = NULL; } return bRet; } BOOL CMSInfoCategory::SaveXML(CMSInfoTextFile* pTxtFile) { CString strData, tmpData; if (!this->m_pParent) { #if defined(_UNICODE) WORD wBom = 0xFEFF; //Unicode Byte Order Mark pTxtFile->m_pFile->Write(&wBom, 2); #endif strData += _T("\r\n\r\n"); CTime tNow = CTime::GetCurrentTime(); CString strTime = tNow.FormatGmt(_T("%x %X")); CString strVersion("7.0"); tmpData.Format(_T("\r\n%s\r\n%s\r\n\r\n"), strVersion, strTime); strData += tmpData; tmpData.Empty(); } CString strName, strCaption; GetNames(&strCaption,&strName); strData += _T("\r\n"); CString strBadXML = _T("& '<>\""); if(SUCCEEDED(m_hrError)) { int iRowCount,iColCount; GetCategoryDimensions(&iColCount, &iRowCount); //TCHAR buf[500] = {0}; //_stprintf(buf, _T("iRowCount=%d iColCount=%d HRESULT=%d m_astrData=%d\r\n"), iRowCount, iColCount, m_hrError, m_astrData); //pTxtFile->WriteString(buf); UINT uiUnused; BOOL fUnused; int iSpaceLoc = 0; CString strColHeader, strRowInfo; if(!iRowCount && (iColCount > 1)) { strData += _T("\r\n"); for(int iCol = 0; iCol < iColCount ; iCol++) { GetColumnInfo(iCol, &strColHeader, &uiUnused, &fUnused, &fUnused); //replace blank spaces with underscores. v-stlowe here is also where we should remove any other //characters that XML won't like, like "'" in French //v-stlowe 7/2/2001 while((iSpaceLoc = strColHeader.FindOneOf(strBadXML)) != -1) strColHeader.SetAt(iSpaceLoc , _T('_')); tmpData.Format(_T("<%s>%s\r\n"), strColHeader, strRowInfo, strColHeader); strData += tmpData; } strData += _T("\r\n"); } for(int iRow = 0;iRow < iRowCount; iRow++) { strData += _T("\r\n"); for(int iCol = 0; iCol < iColCount ; iCol++) { GetColumnInfo(iCol, &strColHeader, &uiUnused, &fUnused, &fUnused); //replace blank spaces with underscores. v-stlowe here is also where we should remove any other //characters that XML won't like, like "'" in French //v-stlowe 7/2/2001 while((iSpaceLoc = strColHeader.FindOneOf(strBadXML)) != -1) strColHeader.SetAt(iSpaceLoc , _T('_')); if(!m_astrData) break; strRowInfo = m_astrData[iRow * m_iColCount + iCol]; tmpData.Format(_T("<%s>\r\n"), strColHeader, strRowInfo, strColHeader); strData += tmpData; } strData += _T("\r\n"); } } else { tmpData.Format(_T("\r\n%d\r\n\r\n"), m_hrError); strData += tmpData; } pTxtFile->WriteString(strData); for(CMSInfoCategory* pChild = this->GetFirstChild(); pChild != NULL; pChild = pChild->GetNextSibling()) pChild->SaveXML(pTxtFile); pTxtFile->WriteString(_T("\r\n")); if (!this->m_pParent) pTxtFile->WriteString(_T("")); return TRUE; } //----------------------------------------------------------------------------- // Prints this category, and recursively prints subcategories, if bRecursive is // true. If nStartPage and nEndPage are 0, page range is ignored (all pages are // printed). If bRecursive is true and a print range is specified, // each category will be processed but only information that would fall on the page range // will be printed //----------------------------------------------------------------------------- void CMSInfoCategory::Print(HDC hDC, BOOL bRecursive,int nStartPage, int nEndPage, LPTSTR lpMachineName) { //nStartPage and nEndPage mark a page range to print; //if both are 0, then print everything CMSInfoPrintHelper* pPrintHelper = new CMSInfoPrintHelper(hDC,nStartPage,nEndPage); //header info..do we need this? // WCHAR wHeader = 0xFEFF; //pTxtFile->Write( &wHeader, 2); try { CTime tNow = CTime::GetCurrentTime(); CString strTimeFormat; strTimeFormat.LoadString(IDS_TIME_FORMAT); CString strHeaderText = tNow.Format(strTimeFormat); pPrintHelper->PrintLine(strHeaderText); if (NULL != lpMachineName) { CString strMachine; strMachine.LoadString(IDS_SYSTEMNAME); strMachine += _tcsupr(lpMachineName); pPrintHelper->PrintLine(strMachine); } Print(pPrintHelper,bRecursive); } catch (CException* pException) { pException->ReportError(); pException->Delete(); } catch(...) { ::AfxSetResourceHandle(_Module.GetResourceInstance()); CString strCaption, strMessage; strCaption.LoadString(IDS_SYSTEMINFO); strMessage.LoadString(IDS_PRINT_GENERIC); ::MessageBox(NULL,strMessage, strCaption,MB_OK); } delete pPrintHelper; } //----------------------------------------------------------------------------- // Prints this category, and recursively prints subcategories, if bRecursive is // true. If nStartPage and nEndPage are 0, page range is ignored (all pages are // printed). If bRecursive is true and a print range is specified, // each category will be processed but only information that would fall on the page range // will be printed //----------------------------------------------------------------------------- void CMSInfoCategory::Print(CMSInfoPrintHelper* pPrintHelper, BOOL bRecursive) { CString strOut; CString strBracket; VERIFY(strBracket.LoadString(IDS_LEFTBRACKET) && "Failed to find resource IDS_LEFTBRACKET"); strOut = strBracket; CString strName, strCaption; GetNames(&strCaption,&strName); strOut += strCaption; VERIFY(strBracket.LoadString(IDS_RIGHTBRACKET) && "Failed to find resource IDS_RIGHTBRACKET"); strOut += strBracket; pPrintHelper->PrintLine(""); pPrintHelper->PrintLine(strOut); int iRowCount,iColCount; this->GetCategoryDimensions(&iColCount,&iRowCount); CString strColHeader; UINT uiUnused; BOOL fUnused; //TD: put in resources CString strColSpacing = " "; pPrintHelper->PrintLine(""); if (1 == iColCount && 0 == iRowCount) { //this is a parent node, with no data of its own CString strCatHeading; strCatHeading.LoadString(IDS_CATEGORYHEADING); pPrintHelper->PrintLine(strCatHeading); } else if (iColCount > 0) { CString strComposite; for(int iCol =0 ; iCol PrintLine(strComposite); strComposite = ""; CString strRowInfo; //for(int iRow = iRowCount - 1; iRow >= 0; iRow--) for(int iRow = 0; iRow < iRowCount; iRow++) { //for(int iCol = iColCount - 1; iCol >= 0 ; iCol--) for(int iCol =0 ; iCol PrintLine(strComposite); strComposite = ""; } } if (bRecursive && this->m_pFirstChild != NULL) { for(CMSInfo5Category* pChild = (CMSInfo5Category*) this->GetFirstChild();pChild != NULL;pChild = (CMSInfo5Category*) pChild->GetNextSibling()) { pChild->Print(pPrintHelper,TRUE); } } } //----------------------------------------------------------------------------- // Prints a line of text (if current page is in print range) and updates positioning // information. If the line is too long to fit on page it is split and printed on // multiple lines //----------------------------------------------------------------------------- extern void StringReplace(CString & str, LPCTSTR szLookFor, LPCTSTR szReplaceWith); void CMSInfoPrintHelper::PrintLine( CString strLine) { //simple line printing function that makes sure that if line exceeds page length, //it will wrap to the next line //m_nCurrentLineIndex will be the line number, not the vertical position //increment the line index, so calling object will print next line //at appropriate vertical position ++m_nCurrentLineIndex; strLine.TrimRight(); //replace tabs with whitespace StringReplace(strLine, _T("\t"), _T(" ")); // strLine.Replace(_T("\t"),_T(" ")); CSize csLinecaps = m_pPrintDC->GetTextExtent(strLine); //see if current position is on page; if not (we've printed to the bottom of the page) //paginate, and reset index to 0 int nFooterMargin = GetFooterMargin(); int nVDeviceCaps = GetDeviceCaps(m_hDC,VERTRES); int nPageVertSize = GetDeviceCaps(m_hDC,VERTRES) - csLinecaps.cy - GetFooterMargin(); if (GetVerticalPos(m_nCurrentLineIndex,csLinecaps) >= nPageVertSize) { Paginate(); if (IsInPageRange(m_nPageNumber)) { StartPage(this->GetHDC()); PrintHeader(); m_bNeedsEndPage = TRUE; } } int nHorzSize = GetDeviceCaps(m_hDC,HORZRES); if (csLinecaps.cx > nHorzSize) { //line is longer than device caps, and needs to be adjusted CString strAdjusted; for(int i = 0;i < strLine.GetLength() ;i++) { strAdjusted += strLine[i]; csLinecaps = m_pPrintDC->GetTextExtent(strAdjusted); if (csLinecaps.cx > nHorzSize) { strAdjusted = strAdjusted.Left(--i); //yPosition will be m_nLineIndex* height of a line //check to see if this page is within print range //if it isn't, we don't want the text to actually go to the printer if (IsInPageRange(m_nPageNumber)) { //pDC->TextOut(this->GetVerticalPos(this->m_nCurrentLineIndex,csLinecaps),0,strAdjusted); //m_pPrintDC->TextOut(0,this->GetVerticalPos(this->m_nCurrentLineIndex,csLinecaps),strAdjusted,strAdjusted.GetLength()); VERIFY(TextOut(m_hDC,0,this->GetVerticalPos(this->m_nCurrentLineIndex,csLinecaps),strAdjusted,strAdjusted.GetLength())); } PrintLine(strLine.Right(strLine.GetLength() -i)); break; } } } else { if (IsInPageRange(m_nPageNumber)) { //for debug...remove int z = this->GetVerticalPos(this->m_nCurrentLineIndex,csLinecaps); VERIFY(TextOut(m_hDC,0,this->GetVerticalPos(this->m_nCurrentLineIndex,csLinecaps),strLine,strLine.GetLength())); //TRACE("%d %d %s\n",z,m_nCurrentLineIndex,strLine); } } } //----------------------------------------------------------------------------- // Manages GDI objects (DC and Font), and information about printing positions // and page ranges //----------------------------------------------------------------------------- CMSInfoPrintHelper::CMSInfoPrintHelper(HDC hDC,int nStartPage, int nEndPage) : m_nStartPage(nStartPage),m_nEndPage(nEndPage),m_nCurrentLineIndex(0),m_nPageNumber(1),m_hDC(hDC) { m_pPrintDC = new CDC(); m_pPrintDC->Attach(hDC); // Create the font for printing. Read font information from string // resources, to allow the localizers to control what font is // used for printing. Set the variables for the default font to use. int nHeight = 10; int nWeight = FW_NORMAL; BYTE nCharSet = DEFAULT_CHARSET; BYTE nPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; CString strFace = "Courier New"; // Load string resources to see if we should use other values // than the defaults. CString strHeight, strWeight, strCharSet, strPitchAndFamily, strFaceName; strHeight.LoadString(IDS_PRINT_FONT_HEIGHT); strWeight.LoadString(IDS_PRINT_FONT_WEIGHT); strCharSet.LoadString(IDS_PRINT_FONT_CHARSET); strPitchAndFamily.LoadString(IDS_PRINT_FONT_PITCHANDFAMILY); strFaceName.LoadString(IDS_PRINT_FONT_FACENAME); if (!strHeight.IsEmpty() && ::_ttol(strHeight)) nHeight = ::_ttoi(strHeight); if (!strWeight.IsEmpty()) nWeight = ::_ttoi(strWeight); if (!strCharSet.IsEmpty()) nCharSet = (BYTE) ::_ttoi(strCharSet); if (!strPitchAndFamily.IsEmpty()) nPitchAndFamily = (BYTE) ::_ttoi(strPitchAndFamily); strFaceName.TrimLeft(); if (!strFaceName.IsEmpty() && strFaceName != CString("facename")) strFace = strFaceName; m_pCurrentFont = new CFont(); nHeight = -((this->m_pPrintDC->GetDeviceCaps (LOGPIXELSY) * nHeight) / 72); VERIFY(this->m_pCurrentFont->CreateFont(nHeight, 0, 0, 0, nWeight, 0, 0, 0, nCharSet, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, nPitchAndFamily, strFace)); m_pOldFont = (CFont*) m_pPrintDC->SelectObject(this->m_pCurrentFont); ASSERT(m_pOldFont && "Error Selecting Font object into CDC"); DOCINFO docinfo; memset(&docinfo, 0, sizeof(docinfo)); docinfo.cbSize = sizeof(docinfo); CString strDocName; strDocName.LoadString(IDS_PRINTING_DOCNAME); docinfo.lpszDocName = strDocName; m_pPrintDC->StartDoc(&docinfo); m_pPrintDC->StartPage(); PrintHeader(); m_bNeedsEndPage = TRUE; } CMSInfoPrintHelper::~CMSInfoPrintHelper() { if (m_bNeedsEndPage) { VERIFY(EndPage(m_pPrintDC->m_hDC)); } int nResult = m_pPrintDC->EndDoc(); ASSERT(nResult >= 0); //reportprinting error //should be if < -1 if (nResult < 0) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); CString strError, strTitle; switch(nResult) { case SP_OUTOFDISK: VERIFY(strError.LoadString(IDS_PRINT_NODISK)); break; case SP_OUTOFMEMORY: VERIFY(strError.LoadString(IDS_PRINT_NOMEMORY)); break; case SP_USERABORT: VERIFY(strError.LoadString(IDS_PRINT_USERABORTED)); break; case SP_ERROR: default: VERIFY(strError.LoadString(IDS_PRINT_GENERIC)); break; } strTitle.LoadString(IDS_DESCRIPTION); ::MessageBox( ::AfxGetMainWnd()->GetSafeHwnd(), strError, strTitle, MB_OK); } m_pPrintDC->SelectObject(m_pOldFont); if (m_pCurrentFont) { delete m_pCurrentFont; } this->m_pPrintDC->Detach(); delete m_pPrintDC; } //----------------------------------------------------------------------------- // Used to calculate where on printed page a line of text should go // nLineIndex is sequenced line number; csLinecaps is size returned by // GetTextExtent for a string of text //----------------------------------------------------------------------------- int CMSInfoPrintHelper::GetVerticalPos(int nLineIndex,CSize csLinecaps) { //returns an int which specifies the vertical position at which a given line of text //should print CString strLinespacing; //spacing is based on string resource IDS_PRINT_LINESPACING strLinespacing.LoadString(IDS_PRINT_LINESPACING); TCHAR** ppStopChr = NULL;//not used double flLineSpacing =_tcstod(strLinespacing,ppStopChr); return (int)(csLinecaps.cy * flLineSpacing )*m_nCurrentLineIndex; } //----------------------------------------------------------------------------- // Performs page load-eject on printer //----------------------------------------------------------------------------- void CMSInfoPrintHelper::Paginate() { //TD: print page number in footer //Do we assume roman numerals for page numbers? //check to see if this page is within print range //if it is, call StartPage and EndPage to make printer spit out paper; //otherwise, just change indexes... if (IsInPageRange(m_nPageNumber)) { //use string resource for page number format CString strPageFooter; CString strPageFormat; strPageFormat.LoadString(IDS_PRINT_FTR_CTR); strPageFooter.Format(strPageFormat,m_nPageNumber); //print number in middle of page int nHorzRes,nVertRes; nHorzRes = m_pPrintDC->GetDeviceCaps(HORZRES); nVertRes = m_pPrintDC->GetDeviceCaps(VERTRES); this->m_pPrintDC->TextOut(nHorzRes / 2,nVertRes - this->GetFooterMargin(),strPageFooter); EndPage(this->GetHDC()); m_bNeedsEndPage = FALSE; } m_nCurrentLineIndex = 0; this->m_nPageNumber++; } //----------------------------------------------------------------------------- // determines if page ranges need to be checked // and if a given page number is in a specified page range //----------------------------------------------------------------------------- BOOL CMSInfoPrintHelper::IsInPageRange(int nPageNumber) { //if both m_nStartPage and m_nEndPage are 0, we are printing all pages if (-1 == m_nStartPage && -1 == m_nEndPage) { return TRUE; } if (nPageNumber >= this->m_nStartPage && nPageNumber <= this->m_nEndPage) { return TRUE; } return FALSE; } //----------------------------------------------------------------------------- //Gets the space to leave at the bottom of the page for page number, etc. //----------------------------------------------------------------------------- int CMSInfoPrintHelper::GetFooterMargin() { //use resource string to set footer margin CString strRes; strRes.LoadString(IDS_PRINT_FTR_CTR ); CSize sizeText = m_pPrintDC->GetTextExtent(strRes); return sizeText.cy; } void CMSInfoPrintHelper::PrintHeader() { CString strHeader; strHeader.LoadString(IDS_PRINT_HDR_RIGHT_CURRENT); CSize sizeString = m_pPrintDC->GetTextExtent(strHeader); int nXPos = m_pPrintDC->GetDeviceCaps(HORZRES) - sizeString.cx; this->m_pPrintDC->TextOut(nXPos,0,strHeader); m_nCurrentLineIndex++; }