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

  1. #include "stdafx.h"
  2. #include "category.h"
  3. #include "MSInfo5Category.h"
  4. #include "FileIO.h"
  5. #include "filestuff.h"
  6. #include <afxtempl.h>
  7. #include "fdi.h"
  8. #include "cabfunc.h"
  9. #include "dataset.h"
  10. #include "resource.h"
  11. #include "msxml.h"
  12. #include "wbemcli.h"
  13. ///////////////
  14. //CMSInfo7Category
  15. CMSInfo7Category::CMSInfo7Category()
  16. {
  17. this->m_iColCount = 0;
  18. this->m_iRowCount = 0;
  19. this->m_pFirstChild = NULL;
  20. this->m_pNextSibling = NULL;
  21. this->m_pParent = NULL;
  22. this->m_pPrevSibling = NULL;
  23. }
  24. CMSInfo7Category::~CMSInfo7Category()
  25. {
  26. this->DeleteAllContent();
  27. }
  28. //-----------------------------------------------------------------------------
  29. // Static member of CMSInfo7Category
  30. // Starts the process of reading a file, creating a new CMSInfo7Category object
  31. // for each category found, and returning a pointer to the root node
  32. //-----------------------------------------------------------------------------
  33. HRESULT CMSInfo7Category::ReadMSI7NFO(CMSInfo7Category** ppRootCat, LPCTSTR szFilename)
  34. {
  35. HRESULT hr = E_FAIL;
  36. CMSInfo7Category* pRootCat = new CMSInfo7Category();
  37. do
  38. {
  39. if (!pRootCat)
  40. break;
  41. if (!pRootCat->LoadFromXML(szFilename))
  42. {
  43. delete pRootCat;
  44. pRootCat = NULL;
  45. break;
  46. }
  47. if (szFilename)
  48. {
  49. CString strAppend;
  50. strAppend.Format(_T(" (%s)"), szFilename);
  51. pRootCat->m_strCaption += strAppend;
  52. }
  53. hr = S_OK;
  54. }
  55. while (false);
  56. *ppRootCat = pRootCat;
  57. return hr;
  58. }
  59. BOOL CMSInfo7Category::LoadFromXML(LPCTSTR szFilename)
  60. {
  61. CComPtr<IXMLDOMDocument> pDoc;
  62. CComPtr<IXMLDOMNode> pNode;
  63. HRESULT hr;
  64. VARIANT_BOOL vb;
  65. BOOL retVal = FALSE;
  66. CoInitialize(NULL);
  67. do
  68. {
  69. hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&pDoc);
  70. if (FAILED(hr) || !pDoc)
  71. break;
  72. pDoc->put_async(VARIANT_FALSE);
  73. hr = pDoc->load(CComVariant(szFilename), &vb);
  74. if (FAILED(hr) || !vb)
  75. break;
  76. hr = pDoc->QueryInterface(IID_IXMLDOMNode, (void**)&pNode);
  77. if (FAILED(hr) || !pNode)
  78. break;
  79. if (FAILED(WalkTree(pNode, FALSE)))
  80. break;
  81. retVal = TRUE;
  82. }while (false);
  83. CoUninitialize();
  84. return retVal;
  85. }
  86. HRESULT CMSInfo7Category::WalkTree(IXMLDOMNode* node, BOOL bCreateCategory)
  87. {
  88. CComPtr<IXMLDOMNodeList> childList, rowList;
  89. IXMLDOMNodeList* columnList = NULL;
  90. CComPtr<IXMLDOMNamedNodeMap> attributeMap;
  91. IXMLDOMNode* pNextChild = NULL, *pNextAttribute = NULL, *pNextColumn = NULL;
  92. //CComBSTR xmlStr;
  93. //node->get_xml(&xmlStr);
  94. //read attributes, data & recurse for child categories.
  95. //attributes
  96. if (SUCCEEDED(node->get_attributes(&attributeMap)) && attributeMap != NULL)
  97. {
  98. attributeMap->nextNode(&pNextAttribute);
  99. while (pNextAttribute)
  100. {
  101. CComBSTR bstrName, bstrValue;
  102. pNextAttribute->get_nodeName(&bstrName);
  103. pNextAttribute->get_text(&bstrValue);
  104. if (lstrcmpiW(bstrName, L"Name") == 0)
  105. {
  106. m_strName = bstrValue;
  107. m_strCaption = m_strName;
  108. pNextAttribute->Release();
  109. break;
  110. }
  111. pNextAttribute->Release();
  112. attributeMap->nextNode(&pNextAttribute);
  113. }
  114. }
  115. m_iColCount = 0;
  116. m_iRowCount = 0;
  117. //count number of rows
  118. if (SUCCEEDED(node->selectNodes(CComBSTR("Data"), &rowList)) && rowList != NULL)
  119. {
  120. long len = 0;
  121. rowList->get_length(&len);
  122. m_iRowCount = len;
  123. }
  124. if (m_iRowCount > 0)
  125. m_afRowAdvanced = new BOOL[m_iRowCount];
  126. int iRow = 0;
  127. CMSInfo7Category* pPrevCat = NULL;
  128. //children
  129. if (SUCCEEDED(node->get_childNodes(&childList)) && childList != NULL)
  130. {
  131. childList->nextNode(&pNextChild);
  132. while (pNextChild)
  133. {
  134. CComBSTR bstrName;
  135. pNextChild->get_nodeName(&bstrName);
  136. if ((lstrcmpiW(bstrName, L"Data") == 0) && (m_iRowCount > 0))
  137. {
  138. //for each data(row), read column name & value
  139. if (SUCCEEDED(pNextChild->get_childNodes(&columnList)) && columnList != NULL)
  140. {
  141. BOOL bColumnsInitialized = m_acolumns ? TRUE : FALSE;
  142. if (!bColumnsInitialized)//column static data. do once
  143. {
  144. long len = 0;
  145. columnList->get_length(&len);
  146. m_iColCount = len;
  147. m_fDynamicColumns = TRUE;//for correct deletion.
  148. if (m_iColCount > 0)
  149. m_acolumns = new CMSInfoColumn[m_iColCount];
  150. else
  151. m_acolumns = NULL;
  152. if (m_iColCount > 0 && m_iRowCount > 0)
  153. {
  154. m_astrData = new CString[m_iColCount * m_iRowCount];
  155. m_adwData = new DWORD[m_iColCount * m_iRowCount];
  156. }
  157. else
  158. {
  159. m_astrData = NULL;
  160. m_adwData = NULL;
  161. }
  162. }
  163. int iColumn = 0;
  164. columnList->nextNode(&pNextColumn);
  165. while (pNextColumn)
  166. {
  167. CComBSTR bstrColHdr, bstrRowVal;
  168. if (!bColumnsInitialized)//column static data. do once
  169. {
  170. pNextColumn->get_nodeName(&bstrColHdr);//column hdr
  171. m_acolumns[iColumn].m_strCaption = bstrColHdr;
  172. m_acolumns[iColumn].m_uiWidth = 150;//PENDING
  173. m_acolumns[iColumn].m_fSorts = FALSE;//PENDING
  174. m_acolumns[iColumn].m_fLexical = FALSE;//PENDING
  175. m_acolumns[iColumn].m_fAdvanced = FALSE;//PENDING
  176. m_acolumns[iColumn].m_uiCaption = 0;
  177. }
  178. pNextColumn->get_text(&bstrRowVal);//row value for the column
  179. if(lstrcmpiW(bstrColHdr, L"MSINFOERROR") == 0)
  180. m_hrError = _ttoi(bstrRowVal);
  181. m_astrData[iRow * m_iColCount + iColumn] = bstrRowVal;
  182. m_afRowAdvanced[iRow] = FALSE;//PENDING
  183. if (m_acolumns[iColumn].m_fSorts && !m_acolumns[iColumn].m_fLexical)
  184. {
  185. //m_adwData[iRow * m_iColCount + iColumn] = uiSortOrder;//PENDING
  186. }
  187. iColumn++;
  188. pNextColumn->Release();
  189. columnList->nextNode(&pNextColumn);
  190. }
  191. }
  192. if (columnList)
  193. columnList->Release();
  194. iRow++;
  195. }
  196. else if ((lstrcmpiW(bstrName, L"XML") == 0) || (lstrcmpiW(bstrName, L"MSInfo") == 0))
  197. {
  198. //get past <xml> & <msinfo>
  199. this->WalkTree(pNextChild, bCreateCategory);
  200. }
  201. else if (lstrcmpiW(bstrName, L"Category") == 0)
  202. {
  203. if (!bCreateCategory)
  204. {
  205. bCreateCategory = TRUE;//First category encountered. Subsequent categories get their own node.
  206. this->WalkTree(pNextChild, bCreateCategory);
  207. }
  208. else
  209. {
  210. CMSInfo7Category* pNewCat = new CMSInfo7Category();
  211. pNewCat->SetParent(this);
  212. pNewCat->SetPrevSibling(pPrevCat);
  213. if (pPrevCat)
  214. pPrevCat->SetNextSibling(pNewCat);
  215. else
  216. m_pFirstChild = pNewCat;
  217. pPrevCat = pNewCat;
  218. pNewCat->WalkTree(pNextChild, bCreateCategory);
  219. }
  220. }
  221. pNextChild->Release();
  222. childList->nextNode(&pNextChild);
  223. }
  224. }
  225. return S_OK;
  226. }
  227. // we want these msgs to look very similar to those displayed by the live category
  228. void CMSInfo7Category::GetErrorText(CString * pstrTitle, CString * pstrMessage)
  229. {
  230. if (SUCCEEDED(m_hrError))
  231. {
  232. ASSERT(0 && "why call GetErrorText for no error?");
  233. CMSInfoCategory::GetErrorText(pstrTitle, pstrMessage);
  234. return;
  235. }
  236. if (pstrTitle)
  237. pstrTitle->LoadString(IDS_CANTCOLLECT);
  238. if (pstrMessage)
  239. {
  240. switch (m_hrError)
  241. {
  242. case WBEM_E_OUT_OF_MEMORY:
  243. pstrMessage->LoadString(IDS_OUTOFMEMERROR);
  244. break;
  245. case WBEM_E_ACCESS_DENIED:
  246. pstrMessage->LoadString(IDS_GATHERACCESS_LOCAL);
  247. break;
  248. case WBEM_E_INVALID_NAMESPACE:
  249. pstrMessage->LoadString(IDS_BADSERVER_LOCAL);
  250. break;
  251. case 0x800706BA: // RPC Server Unavailable
  252. case WBEM_E_TRANSPORT_FAILURE:
  253. pstrMessage->LoadString(IDS_NETWORKERROR_LOCAL);
  254. break;
  255. case WBEM_E_FAILED:
  256. case WBEM_E_INVALID_PARAMETER:
  257. default:
  258. pstrMessage->LoadString(IDS_UNEXPECTED);
  259. }
  260. #ifdef _DEBUG
  261. {
  262. CString strTemp;
  263. strTemp.Format(_T("\n\r\n\rDebug Version Only: [HRESULT = 0x%08X]"), m_hrError);
  264. *pstrMessage += strTemp;
  265. }
  266. #endif
  267. }
  268. }
  269. ///////////////
  270. //EO CMSInfo7Category
  271. CMSInfo5Category::CMSInfo5Category()
  272. {
  273. this->m_pFirstChild = NULL;
  274. this->m_pNextSibling = NULL;
  275. this->m_pParent = NULL;
  276. this->m_pPrevSibling = NULL;
  277. }
  278. //-----------------------------------------------------------------------------
  279. // Saves the actual data (by row and column) to the file
  280. //
  281. //-----------------------------------------------------------------------------
  282. void CMSInfoCategory::SaveElements(CMSInfoFile *pFile)
  283. {
  284. CString szWriteString;
  285. MSIColumnSortType stColumn;
  286. unsigned iColCount;
  287. unsigned iRowCount;
  288. GetCategoryDimensions((int*) &iColCount,(int*) &iRowCount);
  289. DataComplexity dcAdvanced;
  290. CArray <int, int &> aColumnValues;
  291. pFile->WriteUnsignedInt(iColCount);
  292. if (iColCount == 0)
  293. return;
  294. //for(unsigned iCol = 0; iCol < iColCount; iCol++)
  295. for(int iCol = iColCount - 1; iCol >= 0 ; iCol--)
  296. {
  297. unsigned uWidth;
  298. BOOL bSort,bLexical;
  299. GetColumnInfo(iCol,&szWriteString,&uWidth,&bSort,&bLexical);
  300. if (bSort)
  301. {
  302. if (bLexical)
  303. {
  304. stColumn = LEXICAL;
  305. }
  306. else
  307. {
  308. stColumn = BYVALUE;
  309. }
  310. }
  311. else
  312. {
  313. stColumn = NOSORT;
  314. }
  315. if (IsColumnAdvanced(iCol))
  316. {
  317. dcAdvanced = ADVANCED;
  318. }
  319. else
  320. {
  321. dcAdvanced = BASIC;
  322. }
  323. if (stColumn == BYVALUE)
  324. {
  325. aColumnValues.Add(iCol);
  326. }
  327. pFile->WriteUnsignedInt(uWidth);
  328. pFile->WriteString(szWriteString);
  329. pFile->WriteUnsignedInt((unsigned) stColumn);
  330. pFile->WriteByte((BYTE)dcAdvanced);
  331. }
  332. int wNextColumn = -1;
  333. unsigned iArray = 0;
  334. pFile->WriteUnsignedInt(iRowCount);
  335. //for(int iRow = 0; iRow < (int) iRowCount; iRow++)
  336. for(int iRow = iRowCount - 1; iRow >= 0 ; iRow--)
  337. {
  338. if (IsRowAdvanced(iRow))
  339. {
  340. dcAdvanced = ADVANCED;
  341. }
  342. else
  343. {
  344. dcAdvanced = BASIC;
  345. }
  346. pFile->WriteByte((BYTE)dcAdvanced);
  347. }
  348. // Iterate over columns, writing sort indices for BYVALUE columns.
  349. DWORD dwSortIndex;
  350. //for(iCol = 0; iCol < iColCount; iCol++)
  351. for(iCol = iColCount - 1; iCol >= 0 ; iCol--)
  352. {
  353. //following variables are not used except for sort info
  354. CString strUnused;
  355. UINT iWidth;
  356. BOOL fSorts;
  357. BOOL fLexical;
  358. GetColumnInfo(iCol,&strUnused,&iWidth,&fSorts,&fLexical);
  359. CDWordArray arySortIndices;
  360. //for(unsigned iRow = 0; iRow < iRowCount; iRow++)
  361. for(int iRow = iRowCount - 1; iRow >= 0 ; iRow--)
  362. {
  363. CString * pstrData;
  364. this->GetData(iRow, iCol, &pstrData, &dwSortIndex);
  365. // dwSortIndex = m_adwData[iRow * m_iColCount + iCol];
  366. if (fSorts && !fLexical)
  367. {
  368. arySortIndices.Add(dwSortIndex);
  369. }
  370. // szWriteString = m_astrData[iRow * m_iColCount + iCol];
  371. pFile->WriteString(*pstrData);
  372. }
  373. if (fSorts && !fLexical)
  374. {
  375. ASSERT((unsigned) arySortIndices.GetSize() == iRowCount && "wrong number of Sort indices");
  376. //for(unsigned iRow = 0; iRow < iRowCount; iRow++)
  377. for(int iRow = iRowCount - 1; iRow >= 0 ; iRow--)
  378. {
  379. pFile->WriteUnsignedLong(arySortIndices.GetAt(iRow));
  380. }
  381. }
  382. }
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Fills the various data structures with information in a msinfo file
  386. //-----------------------------------------------------------------------------
  387. BOOL CMSInfo5Category::LoadFromNFO(CMSInfoFile* pFile)
  388. {
  389. //TD: check validity of the file
  390. try
  391. {
  392. pFile->ReadString(this->m_strName);
  393. this->m_strCaption = this->m_strName;
  394. pFile->ReadSignedInt(this->m_iColCount);
  395. if (m_iColCount == 0)
  396. {
  397. this->m_iRowCount = 0;
  398. return TRUE;
  399. }
  400. this->m_acolumns = new CMSInfoColumn[m_iColCount];
  401. for(int iColumn = m_iColCount - 1; iColumn >= 0; iColumn--)
  402. {
  403. UINT uiWidth;
  404. pFile->ReadUnsignedInt(uiWidth);
  405. CString strCaption;
  406. pFile->ReadString(strCaption);
  407. unsigned wSortType;
  408. pFile->ReadUnsignedInt(wSortType);
  409. BOOL fSorts;
  410. BOOL fLexical;
  411. if ( NOSORT == wSortType)
  412. {
  413. fLexical = FALSE;
  414. fSorts = FALSE;
  415. }
  416. else if (BYVALUE == wSortType)
  417. {
  418. fLexical = FALSE;
  419. fSorts = TRUE;
  420. }
  421. else
  422. {
  423. fLexical = TRUE;
  424. fSorts = TRUE;
  425. }
  426. BOOL fAdvanced;
  427. BYTE btAdvanced;
  428. pFile->ReadByte(btAdvanced);
  429. fAdvanced = (BOOL) btAdvanced;
  430. m_acolumns[iColumn].m_strCaption = strCaption;
  431. m_acolumns[iColumn].m_uiWidth = uiWidth;
  432. m_acolumns[iColumn].m_fSorts = fSorts;
  433. m_acolumns[iColumn].m_fLexical = fLexical;
  434. m_acolumns[iColumn].m_fAdvanced = fAdvanced;
  435. m_acolumns[iColumn].m_uiCaption = 0;
  436. }
  437. pFile->ReadSignedInt(this->m_iRowCount);
  438. //Nodes that have no data, but only serve as parents to other nodes, have no Rows
  439. //and a column count of 1
  440. m_astrData = new CString[m_iColCount * m_iRowCount];
  441. m_adwData = new DWORD[m_iColCount * m_iRowCount];
  442. m_afRowAdvanced = new BOOL[m_iRowCount];
  443. //for(int iRow = 0; iRow < m_iRowCount; iRow++)
  444. for(int iRow = m_iRowCount - 1; iRow >=0; iRow--)
  445. {
  446. BYTE bComplexity;
  447. pFile->ReadByte(bComplexity);
  448. if (BASIC == bComplexity)
  449. {
  450. this->m_afRowAdvanced[iRow] = FALSE;
  451. }
  452. else
  453. {
  454. this->m_afRowAdvanced[iRow] = TRUE;
  455. }
  456. }
  457. for(iColumn = m_iColCount - 1; iColumn >= 0; iColumn--)
  458. {
  459. CMSInfoColumn* pCol = &this->m_acolumns[(unsigned)iColumn];
  460. //for(iRow = 0; iRow < this->m_iRowCount; iRow++)
  461. for(int iRow = m_iRowCount - 1; iRow >=0; iRow--)
  462. {
  463. CString strData;
  464. pFile->ReadString(strData);
  465. m_astrData[iRow * m_iColCount + iColumn] = strData;
  466. }
  467. //sort values are another row of ints like Complexity
  468. //for(iRow = 0; iRow < this->m_iRowCount; iRow++)
  469. for(iRow = m_iRowCount - 1; iRow >=0; iRow--)
  470. {
  471. CMSInfoColumn* pColInfo = &this->m_acolumns[iColumn];
  472. if (pColInfo->m_fSorts && !pColInfo->m_fLexical)
  473. {
  474. unsigned uiSortOrder;
  475. pFile->ReadUnsignedInt(uiSortOrder);
  476. m_adwData[iRow * m_iColCount + iColumn] = uiSortOrder;
  477. }
  478. }
  479. }
  480. }
  481. //TD: exception handling
  482. catch (CFileException* pException)
  483. {
  484. pException->ReportError();
  485. pException->Delete();
  486. return FALSE;
  487. }
  488. catch (CFileFormatException* pException)
  489. {
  490. pException->Delete();
  491. return FALSE;
  492. }
  493. catch (...)
  494. {
  495. ::AfxSetResourceHandle(_Module.GetResourceInstance());
  496. //messaging is actually handled elsewhere
  497. /*CString strCaption, strMessage;
  498. strCaption.LoadString(IDS_SYSTEMINFO);
  499. strMessage.LoadString(IDS_BADNFOFILE);
  500. ::MessageBox(NULL,strMessage, strCaption,MB_OK);*/
  501. return FALSE;
  502. }
  503. return TRUE;
  504. }
  505. //-----------------------------------------------------------------------------
  506. // Read the header information found at the beginning of the file
  507. //-----------------------------------------------------------------------------
  508. BOOL ReadMSI5NFOHeader(CMSInfoFile* pFile)
  509. {
  510. unsigned iMsinfoFileVersion;
  511. try
  512. {
  513. pFile->ReadUnsignedInt(iMsinfoFileVersion);
  514. if (iMsinfoFileVersion == CMSInfoFile::VERSION_500_MAGIC_NUMBER)
  515. {
  516. unsigned uVersion;
  517. pFile->ReadUnsignedInt(uVersion);
  518. ASSERT(uVersion == 0x500 && "Version number does not match format #");
  519. if (uVersion != 0x500)
  520. {
  521. return NULL;
  522. }
  523. }
  524. else
  525. {
  526. return NULL;
  527. }
  528. LONG l;
  529. pFile->ReadLong(l); // Save time.
  530. time_t tsSaveTime = (ULONG) l;
  531. //TD: sanity test on date
  532. CString szUnused;
  533. pFile->ReadString(szUnused); // Network machine name
  534. pFile->ReadString(szUnused); // Network user name
  535. }
  536. catch (CFileException* pException)
  537. {
  538. pException->ReportError();
  539. pException->Delete();
  540. return FALSE;
  541. }
  542. catch (CFileFormatException* pException)
  543. {
  544. //TD: exception handling
  545. pException->Delete();
  546. return FALSE;
  547. }
  548. catch (...)
  549. {
  550. //messagebox the user in OpenMSInfoFile
  551. return FALSE;
  552. }
  553. return TRUE;
  554. }
  555. CMSInfo5Category::~CMSInfo5Category()
  556. {
  557. this->DeleteAllContent();
  558. };
  559. //-----------------------------------------------------------------------------
  560. // Static member of CMSInfo5Category
  561. // Starts the process of reading a file, creating a new CMSInfo5Category object
  562. // for each category found, and returning a pointer to the root node
  563. //-----------------------------------------------------------------------------
  564. HRESULT CMSInfo5Category::ReadMSI5NFO(HANDLE hFile,CMSInfo5Category** ppRootCat, LPCTSTR szFilename)
  565. {
  566. CMSInfo5Category* pRootCat = new CMSInfo5Category();
  567. CFile* pFile = new CFile((INT_PTR) hFile);
  568. CMSInfoFile msiFile(pFile);
  569. unsigned iNodeData;
  570. if (!ReadMSI5NFOHeader(&msiFile))
  571. {
  572. //make sure this gets in 2/14 checkin!
  573. /* CString strCaption, strMessage;
  574. ::AfxSetResourceHandle(_Module.GetResourceInstance());
  575. strCaption.LoadString(IDS_SYSTEMINFO);
  576. strMessage.LoadString(IDS_BADNFOFILE);
  577. MessageBox(NULL,strMessage, strCaption,MB_OK);*/
  578. return E_FAIL;
  579. }
  580. try
  581. {
  582. CMSInfo5Category* pCat = NULL;
  583. CMSInfo5Category* pPreviousCat;
  584. if (!pRootCat->LoadFromNFO(&msiFile))
  585. {
  586. delete pRootCat;
  587. pRootCat = NULL;
  588. CString strCaption, strMessage;
  589. ::AfxSetResourceHandle(_Module.GetResourceInstance());
  590. strCaption.LoadString(IDS_SYSTEMINFO);
  591. strMessage.LoadString(IDS_BADNFOFILE);
  592. MessageBox(NULL,strMessage, strCaption,MB_OK);
  593. return E_FAIL;
  594. }
  595. //there will be a dummy System Information node, with colcount of 1 and rowcount of 0
  596. //we need to discard this.
  597. if (pRootCat->m_iColCount == 1 && pRootCat->m_iRowCount == 0)
  598. {
  599. delete pRootCat;
  600. pRootCat = new CMSInfo5Category();
  601. msiFile.ReadUnsignedInt(iNodeData);
  602. if (!pRootCat->LoadFromNFO(&msiFile))
  603. { delete pRootCat;
  604. pRootCat = NULL;
  605. CString strCaption, strMessage;
  606. ::AfxSetResourceHandle(_Module.GetResourceInstance());
  607. strCaption.LoadString(IDS_SYSTEMINFO);
  608. strMessage.LoadString(IDS_BADNFOFILE);
  609. MessageBox(NULL,strMessage, strCaption,MB_OK);
  610. return E_FAIL;
  611. }
  612. }
  613. if (szFilename)
  614. {
  615. CString strAppend;
  616. strAppend.Format(_T(" (%s)"), szFilename);
  617. pRootCat->m_strCaption += strAppend;
  618. }
  619. pPreviousCat = pRootCat;
  620. unsigned iNextNodeType = CMSInfo5Category::FIRST;
  621. //iNextNodeType specifies where in the node tree to put the category
  622. for(;iNextNodeType != CMSInfo5Category::END;)
  623. {
  624. msiFile.ReadUnsignedInt(iNodeData);
  625. if (pPreviousCat == pRootCat)
  626. {
  627. //disregard this particular node position indicator, since we
  628. //don't want an empty root category like MSInfo 5.0
  629. iNodeData = CMSInfo5Category::CHILD;
  630. }
  631. iNextNodeType = iNodeData & CMSInfo5Category::MASK;
  632. switch (iNextNodeType)
  633. {
  634. case CMSInfo5Category::END:
  635. pPreviousCat->SetNextSibling(NULL);
  636. pPreviousCat->SetFirstChild(NULL);
  637. break;
  638. case CMSInfo5Category::NEXT:
  639. pCat = new CMSInfo5Category();
  640. if (!pCat->LoadFromNFO(&msiFile))
  641. {
  642. delete pCat;
  643. pCat = NULL;
  644. return E_FAIL;
  645. }
  646. pCat->SetPrevSibling(pPreviousCat);
  647. //the parent of the previous sibling should be the parent for this
  648. if (pPreviousCat)
  649. {
  650. pCat->SetParent((CMSInfo5Category *) pPreviousCat->GetParent());
  651. pPreviousCat->SetNextSibling(pCat);
  652. pCat->SetPrevSibling(pPreviousCat);
  653. }
  654. pPreviousCat = pCat;
  655. break;
  656. case CMSInfo5Category::CHILD:
  657. pCat = new CMSInfo5Category();
  658. if (!pCat->LoadFromNFO(&msiFile))
  659. {
  660. delete pCat;
  661. pCat = NULL;
  662. return E_FAIL;
  663. }
  664. pCat->SetParent(pPreviousCat);
  665. pPreviousCat->SetFirstChild(pCat);
  666. pCat->SetPrevSibling(NULL);
  667. pPreviousCat = pCat;
  668. break;
  669. case CMSInfo5Category::PARENT:
  670. pCat = new CMSInfo5Category();
  671. if (!pCat->LoadFromNFO(&msiFile))
  672. {
  673. delete pCat;
  674. pCat = NULL;
  675. return E_FAIL;
  676. }
  677. //if this a parent, we need to backtrack out of current branch of tree
  678. //to find the appropriate parent, get an index from iNodeData
  679. //and go back that many categories.
  680. unsigned iDepth = (iNodeData & ~CMSInfo5Category::MASK);
  681. for(unsigned i = 0; i < iDepth; i++)
  682. {
  683. pPreviousCat = (CMSInfo5Category *) pPreviousCat->GetParent();
  684. }
  685. if (!pPreviousCat)
  686. {
  687. return E_FAIL;
  688. }
  689. //now move to the end of chain of children
  690. for(;pPreviousCat->GetNextSibling();)
  691. {
  692. pPreviousCat = (CMSInfo5Category *) pPreviousCat->GetNextSibling();
  693. }
  694. pPreviousCat->SetNextSibling(pCat);
  695. pCat->SetParent((CMSInfo5Category *) pPreviousCat->GetParent());
  696. pCat->SetPrevSibling(pPreviousCat);
  697. pCat->SetNextSibling(NULL);
  698. pPreviousCat = pCat;
  699. break;
  700. }
  701. }
  702. }
  703. catch (CFileException* pException)
  704. {
  705. pException->ReportError();
  706. pException->Delete();
  707. return E_FAIL;
  708. }
  709. catch (CFileFormatException* pException)
  710. {
  711. //TD: cleanup
  712. pException->Delete();
  713. return E_FAIL;
  714. }
  715. catch (...)
  716. {
  717. ::AfxSetResourceHandle(_Module.GetResourceInstance());
  718. CString strCaption, strMessage;
  719. strCaption.LoadString(IDS_SYSTEMINFO);
  720. strMessage.LoadString(IDS_BADNFOFILE);
  721. ::MessageBox(NULL,strMessage, strCaption,MB_OK);
  722. return E_FAIL;
  723. }
  724. //no need to delete pFile; it will be cleaned up by CMSInfoFile destructor
  725. *ppRootCat = pRootCat;
  726. return S_OK;
  727. }
  728. //-----------------------------------------------------------------------------
  729. // Saves this category to a MSInfo 5 file, which must already have header information
  730. // written to it
  731. //-----------------------------------------------------------------------------
  732. BOOL CMSInfoCategory::SaveToNFO(CMSInfoFile* pFile)
  733. {
  734. CString strCaption;
  735. GetNames(&strCaption, NULL);
  736. pFile->WriteString(strCaption);
  737. SaveElements(pFile);
  738. return TRUE;
  739. }
  740. HANDLE CMSInfo5Category::GetFileFromCab(CString strFileName)
  741. {
  742. CString strDest;
  743. GetCABExplodeDir(strDest,TRUE,"");
  744. OpenCABFile(strFileName,strDest);
  745. CString strFilename;
  746. FindFileToOpen(strDest,strFilename);
  747. return CreateFile(strFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  748. }
  749. //-----------------------------------------------------------------------------
  750. //Saves category information as text, recursing children in bRecursive is true
  751. //-----------------------------------------------------------------------------
  752. BOOL CMSInfoCategory::SaveAsText(CMSInfoTextFile* pTxtFile, BOOL bRecursive)
  753. {
  754. CString strOut;
  755. CString strBracket;
  756. VERIFY(strBracket.LoadString(IDS_LEFTBRACKET) && "Failed to find resource IDS_LEFTBRACKET");
  757. strOut = strBracket;
  758. CString strName, strCaption;
  759. GetNames(&strCaption,&strName);
  760. strOut += strCaption;
  761. VERIFY(strBracket.LoadString(IDS_RIGHTBRACKET) && "Failed to find resource IDS_RIGHTBRACKET");
  762. strOut += strBracket;
  763. pTxtFile->WriteString("\r\n");
  764. pTxtFile->WriteString(strOut);
  765. int iRowCount,iColCount;
  766. this->GetCategoryDimensions(&iColCount,&iRowCount);
  767. CString strColHeader;
  768. UINT uiUnused;
  769. BOOL fUnused;
  770. CString strColSpacing = "\t";
  771. pTxtFile->WriteString("\r\n");
  772. pTxtFile->WriteString("\r\n");
  773. if (1 == iColCount && 0 == iRowCount)
  774. {
  775. //this is a parent node, with no data of its own
  776. CString strCatHeading;
  777. strCatHeading.LoadString(IDS_CATEGORYHEADING);
  778. pTxtFile->WriteString(strCatHeading);
  779. }
  780. else
  781. {
  782. //for(int iCol = iColCount - 1; iCol >= 0 ; iCol--)
  783. for(int iCol = 0; iCol < iColCount ; iCol++)
  784. {
  785. GetColumnInfo(iCol,&strColHeader,&uiUnused,&fUnused,&fUnused);
  786. pTxtFile->WriteString(strColHeader);
  787. pTxtFile->WriteString(strColSpacing);
  788. }
  789. pTxtFile->WriteString("\r\n");
  790. CString strRowInfo;
  791. //for(int iRow = iRowCount - 1; iRow >= 0; iRow--)
  792. for(int iRow = 0;iRow < iRowCount; iRow++)
  793. {
  794. //for(int iCol = iColCount - 1; iCol >= 0 ; iCol--)
  795. for(int iCol = 0; iCol < iColCount ; iCol++)
  796. {
  797. //this->GetData(iRow,iCol,&strRowInfo,&dwUnused);
  798. if(m_astrData)
  799. strRowInfo = m_astrData[iRow * m_iColCount + iCol];
  800. else
  801. strRowInfo.LoadString(IDS_CANTCOLLECT);
  802. pTxtFile->WriteString(strRowInfo);
  803. pTxtFile->WriteString(strColSpacing);
  804. }
  805. pTxtFile->WriteString("\r\n");
  806. }
  807. }
  808. if (bRecursive && this->m_pFirstChild != NULL)
  809. {
  810. for(CMSInfo5Category* pChild = (CMSInfo5Category*) this->GetFirstChild();pChild != NULL;pChild = (CMSInfo5Category*) pChild->GetNextSibling())
  811. {
  812. pChild->SaveAsText(pTxtFile,TRUE);
  813. }
  814. }
  815. return TRUE;
  816. }
  817. ///////////////////////
  818. //Functions added by a-kjaw
  819. // The code needs to be refined & is copy-paste from SaveAsText code.
  820. //Maybe a good idea to parameterize SaveAsText func.
  821. /*BOOL CMSInfoCategory::SaveAsXml(CMSInfoTextFile* pTxtFile, BOOL bRecursive)
  822. {
  823. //if this has no parent, it is topmost node, so
  824. //if we're writing whole tree (bRecursive is TRUE) then now is when
  825. //we want to write header
  826. if (!this->m_pParent && bRecursive)
  827. {
  828. pTxtFile->WriteString("<?xml version=\"1.0\" ?>\r\n");
  829. pTxtFile->WriteString("<MsInfo>\r\n");
  830. }
  831. CStringArray csarr;
  832. CString strOut;
  833. CString strBracket;
  834. CString strName, strCaption;
  835. GetNames(&strCaption,&strName);
  836. strOut += strCaption;
  837. strOut += strBracket;
  838. pTxtFile->WriteString("<Category name=\"");
  839. pTxtFile->WriteString(strOut);
  840. pTxtFile->WriteString("\">\r\n");
  841. int iRowCount,iColCount;
  842. this->GetCategoryDimensions(&iColCount,&iRowCount);
  843. CString strColHeader;
  844. UINT uiUnused;
  845. BOOL fUnused;
  846. int iSpaceLoc = 0;
  847. if (1 == iColCount && 0 == iRowCount)
  848. {
  849. //this is a parent node, with no data of its own
  850. //CString strCatHeading;
  851. //strCatHeading.LoadString(IDS_CATEGORYHEADING);
  852. //pTxtFile->WriteString("<Category>");
  853. //pTxtFile->WriteString(strCatHeading);
  854. //pTxtFile->WriteString("</Category>\r\n");
  855. }
  856. else
  857. {
  858. //for(int iCol = iColCount - 1; iCol >= 0 ; iCol--)
  859. csarr.RemoveAll();
  860. for(int iCol = 0; iCol < iColCount ; iCol++)
  861. {
  862. //XML wont accept spaces at node names. ie. <Category Name> should be <Category_Name>
  863. GetColumnInfo(iCol,&strColHeader,&uiUnused,&fUnused,&fUnused);
  864. while((iSpaceLoc = strColHeader.Find(_T(" ") , 0)) != -1)
  865. strColHeader.SetAt(iSpaceLoc , _T('_'));
  866. csarr.Add(strColHeader);
  867. }
  868. CString strRowInfo;
  869. for(int iRow = 0;iRow < iRowCount; iRow++)
  870. {
  871. for(int iCol = 0; iCol < iColCount ; iCol++)
  872. {
  873. strRowInfo = m_astrData[iRow * m_iColCount + iCol];
  874. pTxtFile->WriteString("<");
  875. pTxtFile->WriteString(csarr[iCol]);
  876. pTxtFile->WriteString(">");
  877. //Put CDATA here to take care of all weird characters.
  878. pTxtFile->WriteString("<![CDATA[");
  879. pTxtFile->WriteString(strRowInfo);
  880. pTxtFile->WriteString("]]>");
  881. pTxtFile->WriteString("</");
  882. pTxtFile->WriteString(csarr[iCol]);
  883. pTxtFile->WriteString(">\r\n");
  884. }
  885. pTxtFile->WriteString("\r\n");
  886. }
  887. pTxtFile->WriteString("</Category>\r\n");
  888. }
  889. if (bRecursive && this->m_pFirstChild != NULL)
  890. {
  891. for(CMSInfo5Category* pChild = (CMSInfo5Category*) this->GetFirstChild();pChild != NULL;pChild = (CMSInfo5Category*) pChild->GetNextSibling())
  892. {
  893. pChild->SaveAsXml(pTxtFile,TRUE);
  894. }
  895. }
  896. return TRUE;
  897. }*/
  898. //-----------------------------------------------------------------------------
  899. // Saves this category as text to an open file, and recursively saves subcategories
  900. // if bRecursive is true
  901. // Assumes file can be close when last category is written
  902. //-----------------------------------------------------------------------------
  903. BOOL CMSInfoCategory::SaveAsText(HANDLE hFile, BOOL bRecursive, LPTSTR lpMachineName)
  904. {
  905. CFile * pFileOut = new CFile((INT_PTR)hFile);
  906. // The text file is Unicode, so it needs the marker (339423).
  907. WCHAR wUnicodeMarker = 0xFEFF;
  908. pFileOut->Write((const void *)&wUnicodeMarker, sizeof(WCHAR));
  909. try
  910. {
  911. CMSInfoTextFile * pTxtFile = new CMSInfoTextFile(pFileOut);
  912. CTime tNow = CTime::GetCurrentTime();
  913. CString strTimeFormat;
  914. VERIFY(strTimeFormat.LoadString(IDS_TIME_FORMAT) && "Failed to find resource IDS_TIME_FORMAT");
  915. CString strHeaderText = tNow.Format(strTimeFormat);
  916. pTxtFile->WriteString(strHeaderText);
  917. if (NULL != lpMachineName)
  918. {
  919. CString strMachine;
  920. strMachine.LoadString(IDS_SYSTEMNAME);
  921. strMachine += _tcsupr(lpMachineName);
  922. pTxtFile->WriteString(strMachine);
  923. }
  924. if (!this->SaveAsText(pTxtFile,bRecursive))
  925. {
  926. return FALSE;
  927. }
  928. delete pTxtFile;
  929. }
  930. catch(CFileException* pException)
  931. {
  932. pException->ReportError();
  933. pException->Delete();
  934. }
  935. catch (CException* pException)
  936. {
  937. pException->ReportError();
  938. pException->Delete();
  939. }
  940. catch(...)
  941. {
  942. ::AfxSetResourceHandle(_Module.GetResourceInstance());
  943. CString strCaption, strMessage;
  944. strCaption.LoadString(IDS_SYSTEMINFO);
  945. strMessage.LoadString(IDS_FILESAVEERROR_UNKNOWN);
  946. ::MessageBox(NULL,strMessage, strCaption,MB_OK);
  947. }
  948. //CloseHandle(hFile);
  949. return TRUE;
  950. }
  951. ///////////////////////
  952. //Functions added by a-kjaw
  953. // The code needs to be refined & is copy-paste from SaveAsText code.
  954. //Maybe a good idea to parameterize SaveAsText func.
  955. /*BOOL CMSInfoCategory::SaveAsXml(HANDLE hFile, BOOL bRecursive)
  956. {
  957. CFile* pFileOut = new CFile((INT_PTR)hFile);
  958. try
  959. {
  960. CMSInfoTextFile* pTxtFile = new CMSInfoTextFile(pFileOut);
  961. if (!this->SaveAsXml(pTxtFile,bRecursive))
  962. {
  963. return FALSE;
  964. }
  965. pTxtFile->WriteString("</MsInfo>\r\n");
  966. delete pTxtFile;
  967. }
  968. catch(CFileException e)
  969. {
  970. e.ReportError();
  971. }
  972. catch (CException e)
  973. {
  974. e.ReportError();
  975. }
  976. catch(...)
  977. {
  978. ::AfxSetResourceHandle(_Module.GetResourceInstance());
  979. CString strCaption, strMessage;
  980. strCaption.LoadString(IDS_SYSTEMINFO);
  981. strMessage.LoadString(IDS_FILESAVEERROR_UNKNOWN);
  982. ::MessageBox(NULL,strMessage, strCaption,MB_OK);
  983. }
  984. //CloseHandle(hFile);
  985. return TRUE;
  986. }*/
  987. //-----------------------------------------------------------------------------
  988. // Static function that saves specified category to a MSInfo 5 nfo file
  989. // writing header information (so it should be used only to save either root
  990. // category or single category
  991. //-----------------------------------------------------------------------------
  992. BOOL CMSInfoCategory::SaveNFO(HANDLE hFile, CMSInfoCategory* pCategory, BOOL fRecursive)
  993. {
  994. //msiFile will delete pFile in its destructor
  995. try
  996. {
  997. CFile* pFile = new CFile((INT_PTR) hFile);
  998. CMSInfoFile msiFile(pFile);
  999. msiFile.WriteHeader(NULL);
  1000. if (!fRecursive || pCategory->GetParent() != NULL)
  1001. {
  1002. pCategory->SaveToNFO(&msiFile);
  1003. msiFile.WriteEndMark();
  1004. return TRUE;
  1005. }
  1006. CMSInfoCategory* pNext = NULL;
  1007. CMSInfoCategory* pRoot = pCategory;
  1008. //change col and row count of pCategory, to use it as a to create empty node System Information
  1009. //node, saving original col and row count
  1010. int iRowCount, iColCount;
  1011. iRowCount = pCategory->m_iRowCount;
  1012. iColCount = pCategory->m_iColCount;
  1013. pCategory->m_iColCount = 1;
  1014. pCategory->m_iRowCount = 0;
  1015. if (!pCategory->SaveToNFO(&msiFile))
  1016. {
  1017. return FALSE;
  1018. }
  1019. //restore col and row counts
  1020. pCategory->m_iColCount = iColCount;
  1021. pCategory->m_iRowCount = iRowCount;
  1022. //write child mark
  1023. msiFile.WriteChildMark();
  1024. do
  1025. {
  1026. //write the data for each category as it is encountered
  1027. if (!pCategory->SaveToNFO(&msiFile))
  1028. {
  1029. return FALSE;
  1030. }
  1031. //if we have a child, traverse it
  1032. pNext = pCategory->GetFirstChild();
  1033. if (pCategory == pRoot)
  1034. {
  1035. msiFile.WriteNextMark();
  1036. pCategory = pNext;
  1037. continue;
  1038. }
  1039. else if (pNext != NULL)
  1040. {
  1041. msiFile.WriteChildMark();
  1042. pCategory = pNext;
  1043. continue;
  1044. }
  1045. /*if (pCategory == pRoot)
  1046. {
  1047. break;
  1048. }*/
  1049. //if we have reached the bottom of our list, traverse our siblings
  1050. pNext = pCategory->GetNextSibling();
  1051. if (pNext != NULL)
  1052. {
  1053. msiFile.WriteNextMark();
  1054. pCategory = pNext;
  1055. continue;
  1056. }
  1057. //if we have no more siblings, find our nearest parent's sibling, traversing
  1058. //upwards until we find the node we started with
  1059. pNext = pCategory->GetParent();
  1060. ASSERT(pNext != NULL);
  1061. unsigned uParentCount = 0;
  1062. while (pNext != pRoot)
  1063. {
  1064. ++uParentCount;
  1065. pCategory = pNext->GetNextSibling();
  1066. //our parent has a sibling, continue with it
  1067. if (pCategory != NULL)
  1068. {
  1069. msiFile.WriteParentMark(uParentCount);
  1070. break;
  1071. }
  1072. pNext = pNext->GetParent();
  1073. }
  1074. //if we've returned to our root node, we're done
  1075. if (pNext == pRoot)
  1076. {
  1077. break;
  1078. }
  1079. } while (pCategory != NULL);
  1080. msiFile.WriteEndMark();
  1081. }
  1082. catch(CFileException* pException)
  1083. {
  1084. pException->ReportError();
  1085. pException->Delete();
  1086. }
  1087. catch (CException* pException)
  1088. {
  1089. pException->ReportError();
  1090. pException->Delete();
  1091. }
  1092. catch(...)
  1093. {
  1094. ::AfxSetResourceHandle(_Module.GetResourceInstance());
  1095. CString strCaption, strMessage;
  1096. strCaption.LoadString(IDS_SYSTEMINFO);
  1097. strMessage.LoadString(IDS_FILESAVEERROR_UNKNOWN);
  1098. ::MessageBox(NULL,strMessage, strCaption,MB_OK);
  1099. }
  1100. return TRUE;
  1101. }
  1102. BOOL CMSInfoCategory::SaveXML(HANDLE hFile)
  1103. {
  1104. BOOL bRet = FALSE;
  1105. CMSInfoTextFile* pTxtFile = NULL;
  1106. CFile* pFileOut = new CFile((INT_PTR)hFile);
  1107. try
  1108. {
  1109. pTxtFile = new CMSInfoTextFile(pFileOut);
  1110. if (pTxtFile)
  1111. bRet = SaveXML(pTxtFile);
  1112. }
  1113. catch(CFileException* pException)
  1114. {
  1115. pException->ReportError();
  1116. pException->Delete();
  1117. }
  1118. catch (CException* pException)
  1119. {
  1120. pException->ReportError();
  1121. pException->Delete();
  1122. }
  1123. catch(...)
  1124. {
  1125. ::AfxSetResourceHandle(_Module.GetResourceInstance());
  1126. CString strCaption, strMessage;
  1127. strCaption.LoadString(IDS_SYSTEMINFO);
  1128. strMessage.LoadString(IDS_FILESAVEERROR_UNKNOWN);
  1129. ::MessageBox(NULL,strMessage, strCaption,MB_OK);
  1130. }
  1131. if (pTxtFile)
  1132. {
  1133. delete pTxtFile;
  1134. pTxtFile = NULL;
  1135. }
  1136. return bRet;
  1137. }
  1138. BOOL CMSInfoCategory::SaveXML(CMSInfoTextFile* pTxtFile)
  1139. {
  1140. CString strData, tmpData;
  1141. if (!this->m_pParent)
  1142. {
  1143. #if defined(_UNICODE)
  1144. WORD wBom = 0xFEFF; //Unicode Byte Order Mark
  1145. pTxtFile->m_pFile->Write(&wBom, 2);
  1146. #endif
  1147. strData += _T("<?xml version=\"1.0\"?>\r\n<MsInfo>\r\n");
  1148. CTime tNow = CTime::GetCurrentTime();
  1149. CString strTime = tNow.FormatGmt(_T("%x %X"));
  1150. CString strVersion("7.0");
  1151. tmpData.Format(_T("<Metadata>\r\n<Version>%s</Version>\r\n<CreationUTC>%s</CreationUTC>\r\n</Metadata>\r\n"), strVersion, strTime);
  1152. strData += tmpData;
  1153. tmpData.Empty();
  1154. }
  1155. CString strName, strCaption;
  1156. GetNames(&strCaption,&strName);
  1157. strData += _T("<Category name=\"");
  1158. strData += strCaption;
  1159. strData += _T("\">\r\n");
  1160. CString strBadXML = _T("& '<>\"");
  1161. if(SUCCEEDED(m_hrError))
  1162. {
  1163. int iRowCount,iColCount;
  1164. GetCategoryDimensions(&iColCount, &iRowCount);
  1165. //TCHAR buf[500] = {0};
  1166. //_stprintf(buf, _T("iRowCount=%d iColCount=%d HRESULT=%d m_astrData=%d\r\n"), iRowCount, iColCount, m_hrError, m_astrData);
  1167. //pTxtFile->WriteString(buf);
  1168. UINT uiUnused;
  1169. BOOL fUnused;
  1170. int iSpaceLoc = 0;
  1171. CString strColHeader, strRowInfo;
  1172. if(!iRowCount && (iColCount > 1))
  1173. {
  1174. strData += _T("<Data>\r\n");
  1175. for(int iCol = 0; iCol < iColCount ; iCol++)
  1176. {
  1177. GetColumnInfo(iCol, &strColHeader, &uiUnused, &fUnused, &fUnused);
  1178. //replace blank spaces with underscores. v-stlowe here is also where we should remove any other
  1179. //characters that XML won't like, like "'" in French
  1180. //v-stlowe 7/2/2001
  1181. while((iSpaceLoc = strColHeader.FindOneOf(strBadXML)) != -1)
  1182. strColHeader.SetAt(iSpaceLoc , _T('_'));
  1183. tmpData.Format(_T("<%s>%s</%s>\r\n"), strColHeader, strRowInfo, strColHeader);
  1184. strData += tmpData;
  1185. }
  1186. strData += _T("</Data>\r\n");
  1187. }
  1188. for(int iRow = 0;iRow < iRowCount; iRow++)
  1189. {
  1190. strData += _T("<Data>\r\n");
  1191. for(int iCol = 0; iCol < iColCount ; iCol++)
  1192. {
  1193. GetColumnInfo(iCol, &strColHeader, &uiUnused, &fUnused, &fUnused);
  1194. //replace blank spaces with underscores. v-stlowe here is also where we should remove any other
  1195. //characters that XML won't like, like "'" in French
  1196. //v-stlowe 7/2/2001
  1197. while((iSpaceLoc = strColHeader.FindOneOf(strBadXML)) != -1)
  1198. strColHeader.SetAt(iSpaceLoc , _T('_'));
  1199. if(!m_astrData)
  1200. break;
  1201. strRowInfo = m_astrData[iRow * m_iColCount + iCol];
  1202. tmpData.Format(_T("<%s><![CDATA[%s]]></%s>\r\n"), strColHeader, strRowInfo, strColHeader);
  1203. strData += tmpData;
  1204. }
  1205. strData += _T("</Data>\r\n");
  1206. }
  1207. }
  1208. else
  1209. {
  1210. tmpData.Format(_T("<Data>\r\n<MSINFOERROR>%d</MSINFOERROR>\r\n</Data>\r\n"), m_hrError);
  1211. strData += tmpData;
  1212. }
  1213. pTxtFile->WriteString(strData);
  1214. for(CMSInfoCategory* pChild = this->GetFirstChild(); pChild != NULL; pChild = pChild->GetNextSibling())
  1215. pChild->SaveXML(pTxtFile);
  1216. pTxtFile->WriteString(_T("</Category>\r\n"));
  1217. if (!this->m_pParent)
  1218. pTxtFile->WriteString(_T("</MsInfo>"));
  1219. return TRUE;
  1220. }
  1221. //-----------------------------------------------------------------------------
  1222. // Prints this category, and recursively prints subcategories, if bRecursive is
  1223. // true. If nStartPage and nEndPage are 0, page range is ignored (all pages are
  1224. // printed). If bRecursive is true and a print range is specified,
  1225. // each category will be processed but only information that would fall on the page range
  1226. // will be printed
  1227. //-----------------------------------------------------------------------------
  1228. void CMSInfoCategory::Print(HDC hDC, BOOL bRecursive,int nStartPage, int nEndPage, LPTSTR lpMachineName)
  1229. {
  1230. //nStartPage and nEndPage mark a page range to print;
  1231. //if both are 0, then print everything
  1232. CMSInfoPrintHelper* pPrintHelper = new CMSInfoPrintHelper(hDC,nStartPage,nEndPage);
  1233. //header info..do we need this?
  1234. // WCHAR wHeader = 0xFEFF;
  1235. //pTxtFile->Write( &wHeader, 2);
  1236. try
  1237. {
  1238. CTime tNow = CTime::GetCurrentTime();
  1239. CString strTimeFormat;
  1240. strTimeFormat.LoadString(IDS_TIME_FORMAT);
  1241. CString strHeaderText = tNow.Format(strTimeFormat);
  1242. pPrintHelper->PrintLine(strHeaderText);
  1243. if (NULL != lpMachineName)
  1244. {
  1245. CString strMachine;
  1246. strMachine.LoadString(IDS_SYSTEMNAME);
  1247. strMachine += _tcsupr(lpMachineName);
  1248. pPrintHelper->PrintLine(strMachine);
  1249. }
  1250. Print(pPrintHelper,bRecursive);
  1251. }
  1252. catch (CException* pException)
  1253. {
  1254. pException->ReportError();
  1255. pException->Delete();
  1256. }
  1257. catch(...)
  1258. {
  1259. ::AfxSetResourceHandle(_Module.GetResourceInstance());
  1260. CString strCaption, strMessage;
  1261. strCaption.LoadString(IDS_SYSTEMINFO);
  1262. strMessage.LoadString(IDS_PRINT_GENERIC);
  1263. ::MessageBox(NULL,strMessage, strCaption,MB_OK);
  1264. }
  1265. delete pPrintHelper;
  1266. }
  1267. //-----------------------------------------------------------------------------
  1268. // Prints this category, and recursively prints subcategories, if bRecursive is
  1269. // true. If nStartPage and nEndPage are 0, page range is ignored (all pages are
  1270. // printed). If bRecursive is true and a print range is specified,
  1271. // each category will be processed but only information that would fall on the page range
  1272. // will be printed
  1273. //-----------------------------------------------------------------------------
  1274. void CMSInfoCategory::Print(CMSInfoPrintHelper* pPrintHelper, BOOL bRecursive)
  1275. {
  1276. CString strOut;
  1277. CString strBracket;
  1278. VERIFY(strBracket.LoadString(IDS_LEFTBRACKET) && "Failed to find resource IDS_LEFTBRACKET");
  1279. strOut = strBracket;
  1280. CString strName, strCaption;
  1281. GetNames(&strCaption,&strName);
  1282. strOut += strCaption;
  1283. VERIFY(strBracket.LoadString(IDS_RIGHTBRACKET) && "Failed to find resource IDS_RIGHTBRACKET");
  1284. strOut += strBracket;
  1285. pPrintHelper->PrintLine("");
  1286. pPrintHelper->PrintLine(strOut);
  1287. int iRowCount,iColCount;
  1288. this->GetCategoryDimensions(&iColCount,&iRowCount);
  1289. CString strColHeader;
  1290. UINT uiUnused;
  1291. BOOL fUnused;
  1292. //TD: put in resources
  1293. CString strColSpacing = " ";
  1294. pPrintHelper->PrintLine("");
  1295. if (1 == iColCount && 0 == iRowCount)
  1296. {
  1297. //this is a parent node, with no data of its own
  1298. CString strCatHeading;
  1299. strCatHeading.LoadString(IDS_CATEGORYHEADING);
  1300. pPrintHelper->PrintLine(strCatHeading);
  1301. }
  1302. else if (iColCount > 0)
  1303. {
  1304. CString strComposite;
  1305. for(int iCol =0 ; iCol <iColCount ; iCol++)
  1306. {
  1307. GetColumnInfo(iCol,&strColHeader,&uiUnused,&fUnused,&fUnused);
  1308. strComposite += strColHeader;
  1309. strComposite += strColSpacing;
  1310. }
  1311. pPrintHelper->PrintLine(strComposite);
  1312. strComposite = "";
  1313. CString strRowInfo;
  1314. //for(int iRow = iRowCount - 1; iRow >= 0; iRow--)
  1315. for(int iRow = 0; iRow < iRowCount; iRow++)
  1316. {
  1317. //for(int iCol = iColCount - 1; iCol >= 0 ; iCol--)
  1318. for(int iCol =0 ; iCol <iColCount ; iCol++)
  1319. {
  1320. strRowInfo = m_astrData[iRow * m_iColCount + iCol];
  1321. strComposite += strRowInfo;
  1322. strComposite += strColSpacing;
  1323. }
  1324. pPrintHelper->PrintLine(strComposite);
  1325. strComposite = "";
  1326. }
  1327. }
  1328. if (bRecursive && this->m_pFirstChild != NULL)
  1329. {
  1330. for(CMSInfo5Category* pChild = (CMSInfo5Category*) this->GetFirstChild();pChild != NULL;pChild = (CMSInfo5Category*) pChild->GetNextSibling())
  1331. {
  1332. pChild->Print(pPrintHelper,TRUE);
  1333. }
  1334. }
  1335. }
  1336. //-----------------------------------------------------------------------------
  1337. // Prints a line of text (if current page is in print range) and updates positioning
  1338. // information. If the line is too long to fit on page it is split and printed on
  1339. // multiple lines
  1340. //-----------------------------------------------------------------------------
  1341. extern void StringReplace(CString & str, LPCTSTR szLookFor, LPCTSTR szReplaceWith);
  1342. void CMSInfoPrintHelper::PrintLine( CString strLine)
  1343. {
  1344. //simple line printing function that makes sure that if line exceeds page length,
  1345. //it will wrap to the next line
  1346. //m_nCurrentLineIndex will be the line number, not the vertical position
  1347. //increment the line index, so calling object will print next line
  1348. //at appropriate vertical position
  1349. ++m_nCurrentLineIndex;
  1350. strLine.TrimRight();
  1351. //replace tabs with whitespace
  1352. StringReplace(strLine, _T("\t"), _T(" ")); // strLine.Replace(_T("\t"),_T(" "));
  1353. CSize csLinecaps = m_pPrintDC->GetTextExtent(strLine);
  1354. //see if current position is on page; if not (we've printed to the bottom of the page)
  1355. //paginate, and reset index to 0
  1356. int nFooterMargin = GetFooterMargin();
  1357. int nVDeviceCaps = GetDeviceCaps(m_hDC,VERTRES);
  1358. int nPageVertSize = GetDeviceCaps(m_hDC,VERTRES) - csLinecaps.cy - GetFooterMargin();
  1359. if (GetVerticalPos(m_nCurrentLineIndex,csLinecaps) >= nPageVertSize)
  1360. {
  1361. Paginate();
  1362. if (IsInPageRange(m_nPageNumber))
  1363. {
  1364. StartPage(this->GetHDC());
  1365. PrintHeader();
  1366. m_bNeedsEndPage = TRUE;
  1367. }
  1368. }
  1369. int nHorzSize = GetDeviceCaps(m_hDC,HORZRES);
  1370. if (csLinecaps.cx > nHorzSize)
  1371. {
  1372. //line is longer than device caps, and needs to be adjusted
  1373. CString strAdjusted;
  1374. for(int i = 0;i < strLine.GetLength() ;i++)
  1375. {
  1376. strAdjusted += strLine[i];
  1377. csLinecaps = m_pPrintDC->GetTextExtent(strAdjusted);
  1378. if (csLinecaps.cx > nHorzSize)
  1379. {
  1380. strAdjusted = strAdjusted.Left(--i);
  1381. //yPosition will be m_nLineIndex* height of a line
  1382. //check to see if this page is within print range
  1383. //if it isn't, we don't want the text to actually go to the printer
  1384. if (IsInPageRange(m_nPageNumber))
  1385. {
  1386. //pDC->TextOut(this->GetVerticalPos(this->m_nCurrentLineIndex,csLinecaps),0,strAdjusted);
  1387. //m_pPrintDC->TextOut(0,this->GetVerticalPos(this->m_nCurrentLineIndex,csLinecaps),strAdjusted,strAdjusted.GetLength());
  1388. VERIFY(TextOut(m_hDC,0,this->GetVerticalPos(this->m_nCurrentLineIndex,csLinecaps),strAdjusted,strAdjusted.GetLength()));
  1389. }
  1390. PrintLine(strLine.Right(strLine.GetLength() -i));
  1391. break;
  1392. }
  1393. }
  1394. }
  1395. else
  1396. {
  1397. if (IsInPageRange(m_nPageNumber))
  1398. {
  1399. //for debug...remove
  1400. int z = this->GetVerticalPos(this->m_nCurrentLineIndex,csLinecaps);
  1401. VERIFY(TextOut(m_hDC,0,this->GetVerticalPos(this->m_nCurrentLineIndex,csLinecaps),strLine,strLine.GetLength()));
  1402. //TRACE("%d %d %s\n",z,m_nCurrentLineIndex,strLine);
  1403. }
  1404. }
  1405. }
  1406. //-----------------------------------------------------------------------------
  1407. // Manages GDI objects (DC and Font), and information about printing positions
  1408. // and page ranges
  1409. //-----------------------------------------------------------------------------
  1410. CMSInfoPrintHelper::CMSInfoPrintHelper(HDC hDC,int nStartPage, int nEndPage)
  1411. : m_nStartPage(nStartPage),m_nEndPage(nEndPage),m_nCurrentLineIndex(0),m_nPageNumber(1),m_hDC(hDC)
  1412. {
  1413. m_pPrintDC = new CDC();
  1414. m_pPrintDC->Attach(hDC);
  1415. // Create the font for printing. Read font information from string
  1416. // resources, to allow the localizers to control what font is
  1417. // used for printing. Set the variables for the default font to use.
  1418. int nHeight = 10;
  1419. int nWeight = FW_NORMAL;
  1420. BYTE nCharSet = DEFAULT_CHARSET;
  1421. BYTE nPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
  1422. CString strFace = "Courier New";
  1423. // Load string resources to see if we should use other values
  1424. // than the defaults.
  1425. CString strHeight, strWeight, strCharSet, strPitchAndFamily, strFaceName;
  1426. strHeight.LoadString(IDS_PRINT_FONT_HEIGHT);
  1427. strWeight.LoadString(IDS_PRINT_FONT_WEIGHT);
  1428. strCharSet.LoadString(IDS_PRINT_FONT_CHARSET);
  1429. strPitchAndFamily.LoadString(IDS_PRINT_FONT_PITCHANDFAMILY);
  1430. strFaceName.LoadString(IDS_PRINT_FONT_FACENAME);
  1431. if (!strHeight.IsEmpty() && ::_ttol(strHeight))
  1432. nHeight = ::_ttoi(strHeight);
  1433. if (!strWeight.IsEmpty())
  1434. nWeight = ::_ttoi(strWeight);
  1435. if (!strCharSet.IsEmpty())
  1436. nCharSet = (BYTE) ::_ttoi(strCharSet);
  1437. if (!strPitchAndFamily.IsEmpty())
  1438. nPitchAndFamily = (BYTE) ::_ttoi(strPitchAndFamily);
  1439. strFaceName.TrimLeft();
  1440. if (!strFaceName.IsEmpty() && strFaceName != CString("facename"))
  1441. strFace = strFaceName;
  1442. m_pCurrentFont = new CFont();
  1443. nHeight = -((this->m_pPrintDC->GetDeviceCaps (LOGPIXELSY) * nHeight) / 72);
  1444. VERIFY(this->m_pCurrentFont->CreateFont(nHeight, 0, 0, 0, nWeight, 0, 0, 0,
  1445. nCharSet, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS,
  1446. DEFAULT_QUALITY, nPitchAndFamily, strFace));
  1447. m_pOldFont = (CFont*) m_pPrintDC->SelectObject(this->m_pCurrentFont);
  1448. ASSERT(m_pOldFont && "Error Selecting Font object into CDC");
  1449. DOCINFO docinfo;
  1450. memset(&docinfo, 0, sizeof(docinfo));
  1451. docinfo.cbSize = sizeof(docinfo);
  1452. CString strDocName;
  1453. strDocName.LoadString(IDS_PRINTING_DOCNAME);
  1454. docinfo.lpszDocName = strDocName;
  1455. m_pPrintDC->StartDoc(&docinfo);
  1456. m_pPrintDC->StartPage();
  1457. PrintHeader();
  1458. m_bNeedsEndPage = TRUE;
  1459. }
  1460. CMSInfoPrintHelper::~CMSInfoPrintHelper()
  1461. {
  1462. if (m_bNeedsEndPage)
  1463. {
  1464. VERIFY(EndPage(m_pPrintDC->m_hDC));
  1465. }
  1466. int nResult = m_pPrintDC->EndDoc();
  1467. ASSERT(nResult >= 0);
  1468. //reportprinting error
  1469. //should be if < -1
  1470. if (nResult < 0)
  1471. {
  1472. AFX_MANAGE_STATE(::AfxGetStaticModuleState());
  1473. CString strError, strTitle;
  1474. switch(nResult)
  1475. {
  1476. case SP_OUTOFDISK:
  1477. VERIFY(strError.LoadString(IDS_PRINT_NODISK));
  1478. break;
  1479. case SP_OUTOFMEMORY:
  1480. VERIFY(strError.LoadString(IDS_PRINT_NOMEMORY));
  1481. break;
  1482. case SP_USERABORT:
  1483. VERIFY(strError.LoadString(IDS_PRINT_USERABORTED));
  1484. break;
  1485. case SP_ERROR:
  1486. default:
  1487. VERIFY(strError.LoadString(IDS_PRINT_GENERIC));
  1488. break;
  1489. }
  1490. strTitle.LoadString(IDS_DESCRIPTION);
  1491. ::MessageBox( ::AfxGetMainWnd()->GetSafeHwnd(), strError, strTitle, MB_OK);
  1492. }
  1493. m_pPrintDC->SelectObject(m_pOldFont);
  1494. if (m_pCurrentFont)
  1495. {
  1496. delete m_pCurrentFont;
  1497. }
  1498. this->m_pPrintDC->Detach();
  1499. delete m_pPrintDC;
  1500. }
  1501. //-----------------------------------------------------------------------------
  1502. // Used to calculate where on printed page a line of text should go
  1503. // nLineIndex is sequenced line number; csLinecaps is size returned by
  1504. // GetTextExtent for a string of text
  1505. //-----------------------------------------------------------------------------
  1506. int CMSInfoPrintHelper::GetVerticalPos(int nLineIndex,CSize csLinecaps)
  1507. {
  1508. //returns an int which specifies the vertical position at which a given line of text
  1509. //should print
  1510. CString strLinespacing;
  1511. //spacing is based on string resource IDS_PRINT_LINESPACING
  1512. strLinespacing.LoadString(IDS_PRINT_LINESPACING);
  1513. TCHAR** ppStopChr = NULL;//not used
  1514. double flLineSpacing =_tcstod(strLinespacing,ppStopChr);
  1515. return (int)(csLinecaps.cy * flLineSpacing )*m_nCurrentLineIndex;
  1516. }
  1517. //-----------------------------------------------------------------------------
  1518. // Performs page load-eject on printer
  1519. //-----------------------------------------------------------------------------
  1520. void CMSInfoPrintHelper::Paginate()
  1521. {
  1522. //TD: print page number in footer
  1523. //Do we assume roman numerals for page numbers?
  1524. //check to see if this page is within print range
  1525. //if it is, call StartPage and EndPage to make printer spit out paper;
  1526. //otherwise, just change indexes...
  1527. if (IsInPageRange(m_nPageNumber))
  1528. {
  1529. //use string resource for page number format
  1530. CString strPageFooter;
  1531. CString strPageFormat;
  1532. strPageFormat.LoadString(IDS_PRINT_FTR_CTR);
  1533. strPageFooter.Format(strPageFormat,m_nPageNumber);
  1534. //print number in middle of page
  1535. int nHorzRes,nVertRes;
  1536. nHorzRes = m_pPrintDC->GetDeviceCaps(HORZRES);
  1537. nVertRes = m_pPrintDC->GetDeviceCaps(VERTRES);
  1538. this->m_pPrintDC->TextOut(nHorzRes / 2,nVertRes - this->GetFooterMargin(),strPageFooter);
  1539. EndPage(this->GetHDC());
  1540. m_bNeedsEndPage = FALSE;
  1541. }
  1542. m_nCurrentLineIndex = 0;
  1543. this->m_nPageNumber++;
  1544. }
  1545. //-----------------------------------------------------------------------------
  1546. // determines if page ranges need to be checked
  1547. // and if a given page number is in a specified page range
  1548. //-----------------------------------------------------------------------------
  1549. BOOL CMSInfoPrintHelper::IsInPageRange(int nPageNumber)
  1550. {
  1551. //if both m_nStartPage and m_nEndPage are 0, we are printing all pages
  1552. if (-1 == m_nStartPage && -1 == m_nEndPage)
  1553. {
  1554. return TRUE;
  1555. }
  1556. if (nPageNumber >= this->m_nStartPage && nPageNumber <= this->m_nEndPage)
  1557. {
  1558. return TRUE;
  1559. }
  1560. return FALSE;
  1561. }
  1562. //-----------------------------------------------------------------------------
  1563. //Gets the space to leave at the bottom of the page for page number, etc.
  1564. //-----------------------------------------------------------------------------
  1565. int CMSInfoPrintHelper::GetFooterMargin()
  1566. {
  1567. //use resource string to set footer margin
  1568. CString strRes;
  1569. strRes.LoadString(IDS_PRINT_FTR_CTR );
  1570. CSize sizeText = m_pPrintDC->GetTextExtent(strRes);
  1571. return sizeText.cy;
  1572. }
  1573. void CMSInfoPrintHelper::PrintHeader()
  1574. {
  1575. CString strHeader;
  1576. strHeader.LoadString(IDS_PRINT_HDR_RIGHT_CURRENT);
  1577. CSize sizeString = m_pPrintDC->GetTextExtent(strHeader);
  1578. int nXPos = m_pPrintDC->GetDeviceCaps(HORZRES) - sizeString.cx;
  1579. this->m_pPrintDC->TextOut(nXPos,0,strHeader);
  1580. m_nCurrentLineIndex++;
  1581. }