Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1832 lines
57 KiB

#include "stdafx.h"
#include "category.h"
#include "MSInfo5Category.h"
#include "FileIO.h"
#include "filestuff.h"
#include <afxtempl.h>
#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<IXMLDOMDocument> pDoc;
CComPtr<IXMLDOMNode> 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<IXMLDOMNodeList> childList, rowList;
IXMLDOMNodeList* columnList = NULL;
CComPtr<IXMLDOMNamedNodeMap> 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 <xml> & <msinfo>
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 <int, int &> 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("<?xml version=\"1.0\" ?>\r\n");
pTxtFile->WriteString("<MsInfo>\r\n");
}
CStringArray csarr;
CString strOut;
CString strBracket;
CString strName, strCaption;
GetNames(&strCaption,&strName);
strOut += strCaption;
strOut += strBracket;
pTxtFile->WriteString("<Category name=\"");
pTxtFile->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("<Category>");
//pTxtFile->WriteString(strCatHeading);
//pTxtFile->WriteString("</Category>\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. <Category Name> should be <Category_Name>
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("<![CDATA[");
pTxtFile->WriteString(strRowInfo);
pTxtFile->WriteString("]]>");
pTxtFile->WriteString("</");
pTxtFile->WriteString(csarr[iCol]);
pTxtFile->WriteString(">\r\n");
}
pTxtFile->WriteString("\r\n");
}
pTxtFile->WriteString("</Category>\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("</MsInfo>\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("<?xml version=\"1.0\"?>\r\n<MsInfo>\r\n");
CTime tNow = CTime::GetCurrentTime();
CString strTime = tNow.FormatGmt(_T("%x %X"));
CString strVersion("7.0");
tmpData.Format(_T("<Metadata>\r\n<Version>%s</Version>\r\n<CreationUTC>%s</CreationUTC>\r\n</Metadata>\r\n"), strVersion, strTime);
strData += tmpData;
tmpData.Empty();
}
CString strName, strCaption;
GetNames(&strCaption,&strName);
strData += _T("<Category name=\"");
strData += strCaption;
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("<Data>\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</%s>\r\n"), strColHeader, strRowInfo, strColHeader);
strData += tmpData;
}
strData += _T("</Data>\r\n");
}
for(int iRow = 0;iRow < iRowCount; iRow++)
{
strData += _T("<Data>\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><![CDATA[%s]]></%s>\r\n"), strColHeader, strRowInfo, strColHeader);
strData += tmpData;
}
strData += _T("</Data>\r\n");
}
}
else
{
tmpData.Format(_T("<Data>\r\n<MSINFOERROR>%d</MSINFOERROR>\r\n</Data>\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("</Category>\r\n"));
if (!this->m_pParent)
pTxtFile->WriteString(_T("</MsInfo>"));
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 <iColCount ; iCol++)
{
GetColumnInfo(iCol,&strColHeader,&uiUnused,&fUnused,&fUnused);
strComposite += strColHeader;
strComposite += strColSpacing;
}
pPrintHelper->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 <iColCount ; iCol++)
{
strRowInfo = m_astrData[iRow * m_iColCount + iCol];
strComposite += strRowInfo;
strComposite += strColSpacing;
}
pPrintHelper->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++;
}