// MSInfo4Category.cpp: implementation of the CMSInfo4Category class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "resource.h" #include "category.h" #include "msictrl.h" #include "datasource.h" #include "MSInfo4Category.h" #include "MSInfo5Category.h" #include "filestuff.h" #include #include "FileIO.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CNFO4DataSource* CMSInfo4Category::s_pNfo4DataSource = NULL; //a-kjaw BOOL CMSInfo4Category::m_bIsControlInstalled = TRUE; //a-kjaw CMSInfo4Category::CMSInfo4Category() : m_pUnknown(NULL) { } CMSInfo4Category::~CMSInfo4Category() { } HRESULT CMSInfo4Category::ReadMSI4NFO(CString strFileName/*HANDLE hFile*/,CMSInfo4Category** ppRootCat) { DWORD grfMode = STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE; CComPtr pStream; CComBSTR bstrFileName(strFileName); CComPtr pStorage; HRESULT hr = StgOpenStorage(bstrFileName, NULL, grfMode, NULL, 0, &pStorage); if (!SUCCEEDED(hr)) { return hr; } CComBSTR bstrMSIStream(_T("MSInfo")); hr = pStorage->OpenStream(bstrMSIStream, NULL, grfMode, 0, &pStream); if (!SUCCEEDED(hr)) { return hr; } COleStreamFile* pOStream; pOStream = new COleStreamFile(pStream); const DWORD MSI_FILE_VER = 0x03000000; DWORD dwVersion, dwCount; ULONG ulCount; // First, read and check the version number in the stream. ulCount = pOStream->Read((void *) &dwVersion, sizeof(DWORD)); if (FAILED(hr) || ulCount != sizeof(DWORD)) { return E_FAIL; } if (dwVersion != MSI_FILE_VER) return E_FAIL; // The next thing in the stream is a set of three strings, each terminated by // a newline character. These three strings are the time/date, machine name and // user name from the saving system. The length of the total string precedes // the string. DWORD dwSize; ulCount = pOStream->Read(&dwSize,sizeof(DWORD)); if ( ulCount != sizeof(DWORD)) return E_FAIL; char * szBuffer = new char[dwSize]; if (szBuffer == NULL) return E_FAIL; ulCount = pOStream->Read((void *) szBuffer,dwSize); if (ulCount != dwSize) { delete szBuffer; return E_FAIL; } // We don't actually care about these values (now at least). /* CString strData(szBuffer, dwSize); m_strTimeDateStamp = strData.SpanExcluding("\n"); strData = strData.Right(strData.GetLength() - m_strTimeDateStamp.GetLength() - 1); m_strMachineName = strData.SpanExcluding("\n"); strData = strData.Right(strData.GetLength() - m_strMachineName.GetLength() - 1); m_strUserName = strData.SpanExcluding("\n"); */ delete szBuffer; // Next, read the map from CLSIDs to stream names. This also includes some // other information about the controls. First we should find a DWORD with // the count of controls. DWORD dwControlCount; ulCount = pOStream->Read(&dwControlCount,sizeof(DWORD)); delete pOStream; if (ulCount != sizeof(int)) return E_FAIL; SAVED_CONTROL_INFO controlInfo; CString strCLSID, strStreamName; //a-stephl CMapStringToString mapStreams; for (DWORD i = 0; i < dwControlCount; i++) { if (FAILED(pStream->Read((void *) &controlInfo, sizeof(SAVED_CONTROL_INFO), &dwCount)) || dwCount != sizeof(SAVED_CONTROL_INFO)) return E_FAIL; strCLSID = controlInfo.szCLSID; strStreamName = controlInfo.szStreamName; // We don't currently care about this information... /* strSize.Format("%ld", controlInfo.dwSize); strInfo.FormatMessage(IDS_OCX_INFO, controlInfo.szName, controlInfo.szVersion, strSize); m_mapCLSIDToInfo.SetAt(strCLSID, strInfo); */ mapStreams.SetAt(strCLSID, strStreamName); } // Read and build the category tree. Read the first level, which must be 0. int iLevel; if (FAILED(pStream->Read((void *) &iLevel, sizeof(int), &dwCount)) || dwCount != sizeof(int)) return E_FAIL; if (iLevel == 0) { LARGE_INTEGER li; li.HighPart = -1; li.LowPart = (ULONG)(0 - sizeof(int)); if (FAILED(pStream->Seek(li, STREAM_SEEK_CUR, NULL))) return E_FAIL; if (!SUCCEEDED(RecurseLoad410Tree(ppRootCat,pStream,pStorage,mapStreams))) { return E_FAIL; } // After RecurseLoadTree is through, we should be able to read a -1 // for the next level. if (FAILED(pStream->Read((void *) &iLevel, sizeof(int), &dwCount)) || dwCount != sizeof(int) || iLevel != -1) { return E_FAIL; } ASSERT(iLevel == -1 && "unexpected value read after RecurseLoadTree"); } else return E_FAIL; CString strAppend; strAppend.Format(_T(" (%s)"), strFileName); (*ppRootCat)->m_strCaption += strAppend; return S_OK; } //----------------------------------------------------------------------------- // This function (which doesn't really use recursion - the name is left over // from 4.10 MSInfo) read the category tree from the MSInfo stream and creates // the necessary COCXFolder objects to represent it. //----------------------------------------------------------------------------- HRESULT CMSInfo4Category::RecurseLoad410Tree(CMSInfo4Category** ppRoot, CComPtr pStream,CComPtr pStorage,CMapStringToString& mapStreams) { // This array of folders is used to keep track of the last folder read // on each level. This is useful for getting the parent and previous // sibling when reading a new folder. CMSInfo4Category* pRoot = NULL; // The iLevel variable keeps track of the current tree level we are // reading a folder for. A -1 indicates the end of the tree. DWORD dwCount; int iLevel = 1; if (FAILED(pStream->Read((void *) &iLevel, sizeof(int), &dwCount)) || dwCount != sizeof(int)) return E_FAIL; int iLastLevel = iLevel; HRESULT hr; CMSInfo4Category* pLastCat = NULL; for(;iLevel != -1;) { CMSInfo4Category* pCat = new CMSInfo4Category(); hr = pCat->LoadFromStream(pStream,pStorage); if (FAILED(hr)) { delete pCat; return hr; } if (!pRoot) { pRoot = pCat; } //CString strCLSID(pCat->m_bstrCLSID); if (!mapStreams.Lookup(pCat->m_strCLSID,pCat->m_strStream)) { ASSERT(1); } if (iLevel == iLastLevel) { pCat->m_pPrevSibling = pLastCat; if (pLastCat) { pCat->m_pParent = pLastCat->m_pParent; pLastCat->m_pNextSibling = pCat; } } else if (iLevel - 1 == iLastLevel) { //we've just stepped from parent to child pCat->m_pPrevSibling = NULL; if (pLastCat) { pCat->m_pParent = pLastCat; pLastCat->m_pFirstChild = pCat; } } else if (iLevel < iLastLevel) { //we need to trace back up chain to find common parent DWORD iLevelDiff = iLastLevel - iLevel; for(DWORD i = 0; i < iLevelDiff; i++) { if (!pLastCat) { break; } pLastCat = (CMSInfo4Category*) pLastCat->m_pParent; } pCat->m_pPrevSibling = pLastCat; if (pLastCat) { pCat->m_pParent = pLastCat->m_pParent; pLastCat->m_pNextSibling = pCat; } } pLastCat = pCat; iLastLevel = iLevel; if (FAILED(pStream->Read((void *) &iLevel, sizeof(int), &dwCount)) || dwCount != sizeof(int)) return E_FAIL; } //a-kjaw if( CMSInfo4Category::m_bIsControlInstalled == FALSE) { CString strCaption, strMessage; ::AfxSetResourceHandle(_Module.GetResourceInstance()); strCaption.LoadString(IDS_SYSTEMINFO); strMessage.LoadString(IDS_NOWI2KRESKIT); ::MessageBox(NULL, strMessage, strCaption, MB_OK | MB_ICONEXCLAMATION); CMSInfo4Category::m_bIsControlInstalled = TRUE; } //a-kjaw *ppRoot = pRoot; // We read a -1 to exit the loop, then we are through with the // category tree. Backup (so any other recursion trees will read // the -1 as well) and return TRUE. if (iLevel == -1) { LARGE_INTEGER li; li.HighPart = -1; li.LowPart = (ULONG)(0 - sizeof(int)); if (FAILED(pStream->Seek(li, STREAM_SEEK_CUR, NULL))) return E_FAIL; } return S_OK; } //----------------------------------------------------------------------------- // This function creates a CMSInfo4Category object based on the information read // from the stream. //----------------------------------------------------------------------------- HRESULT CMSInfo4Category::LoadFromStream(CComPtr pStream,CComPtr pStorage) { // Read in the values from the stream. Make sure they're all there before // we create the COCXFolder. BOOL fUsesView = FALSE; BOOL fControl = FALSE; DWORD dwView = 0; CLSID clsidCategory; char szName[100]; if (FAILED(pStream->Read((void *) &fUsesView, sizeof(BOOL), NULL))) return E_FAIL; if (FAILED(pStream->Read((void *) &fControl, sizeof(BOOL), NULL))) return E_FAIL; if (FAILED(pStream->Read((void *) &dwView, sizeof(DWORD), NULL))) return E_FAIL; if (FAILED(pStream->Read((void *) &clsidCategory, sizeof(CLSID), NULL))) return E_FAIL; if (FAILED(pStream->Read((void *) &szName, sizeof(char) * 100, NULL))) return E_FAIL; // USES_CONVERSION; // LPOLESTR lpName = A2W(szName); this->m_clsid = clsidCategory; ///////a-kjaw CComPtr pUnk; HRESULT hr = S_OK; if( !IsEqualGUID(m_clsid , GUID_NULL) ) hr = CoCreateInstance( m_clsid , NULL, CLSCTX_ALL , IID_IUnknown , (LPVOID*)&pUnk); if (FAILED(hr)) { m_bIsControlInstalled = FALSE; } ///////a-kjaw //StringFromCLSID(this->m_clsid,&m_bstrCLSID); LPOLESTR lpstrCLSID; StringFromCLSID(this->m_clsid,&lpstrCLSID); m_strCLSID = lpstrCLSID; CoTaskMemFree(lpstrCLSID); this->m_pStorage = pStorage; this->m_dwView = dwView; this->m_strName = szName; m_strCaption = szName; return S_OK; } HRESULT CMSInfo4Category::Refresh() { return S_OK; } HRESULT CMSInfo4Category::CreateControl(HWND hWnd,CRect& rct) { try { /*LPOLESTR lpCLSID; if (FAILED(StringFromCLSID(m_clsid, &lpCLSID))) return E_FAIL;*/ HRESULT hr = E_FAIL; if (m_pUnknown == NULL) { hr = CoCreateInstance(m_clsid,NULL,CLSCTX_INPROC_SERVER,IID_IUnknown,(void**) &m_pUnknown); } // Get the stream for this control, and load it. if (!SUCCEEDED(hr)) { return hr; } DWORD grfMode = STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE; CComPtr pStream; USES_CONVERSION; CComBSTR bstrStream = m_strStream;//A2W(m_strStream); hr = m_pStorage->OpenStream(bstrStream, NULL, grfMode, 0, &pStream); if (!SUCCEEDED(hr)) { return hr; } else { COleStreamFile olefile(pStream.Detach()); CMSIControl* p4Ctrl = new CMSIControl(m_clsid); CWnd* pWnd = CWnd::FromHandle(hWnd); AfxEnableControlContainer(); //if (!p4Ctrl->Create(NULL, WS_VISIBLE | WS_CHILD, rct, pWnd, 0, &olefile, FALSE, NULL)) if (!p4Ctrl->Create(NULL, /*WS_VISIBLE |*/ WS_CHILD, rct, pWnd, 0, &olefile, FALSE, NULL)) { return E_FAIL; } olefile.Close(); p4Ctrl->MSInfoUpdateView(); p4Ctrl->MSInfoRefresh(); //Add Control and CLSID to map of CLSID's CMSInfo4Category::s_pNfo4DataSource->AddControlMapping(m_strCLSID,p4Ctrl); } } catch (COleException* pException) { ASSERT(0); pException->Delete(); } catch (CException* pException) { ASSERT(0); pException->Delete(); } catch (...) { ASSERT(0); } return S_OK; } //--------------------------------------------------------------------------- // GetDISPID returns the DISPID for a given string, by looking it up using // IDispatch->GetIDsOfNames. This avoids hardcoding DISPIDs in this class. //--------------------------------------------------------------------------- BOOL CMSInfo4Category::GetDISPID(IDispatch * pDispatch, LPOLESTR szMember, DISPID *pID) { BOOL result = FALSE; DISPID dispid; if (SUCCEEDED(pDispatch->GetIDsOfNames(IID_NULL, &szMember, 1, LOCALE_SYSTEM_DEFAULT, &dispid))) { *pID = dispid; result = TRUE; } return result; } HRESULT CMSInfo4Category::ShowControl(HWND hWnd, CRect rctList, BOOL fShow) { try { //CString strCLSID(m_bstrCLSID); CMSIControl* p4Ctrl = NULL; if (!CMSInfo4Category::s_pNfo4DataSource->GetControlFromCLSID(m_strCLSID,p4Ctrl)) { if (!SUCCEEDED(CreateControl(hWnd,rctList))) { //could not serialize control return E_FAIL; } if(!CMSInfo4Category::s_pNfo4DataSource->GetControlFromCLSID(m_strCLSID,p4Ctrl)) { if (!IsDisplayableCategory()) { //this is one of the parent nodes, which does not display info CMSInfo4Category::s_pNfo4DataSource->UpdateCurrentControl(NULL); return S_OK; } return E_FAIL; } } else { ResizeControl(rctList); } ASSERT(p4Ctrl && "Invalid OCX pointer"); if (fShow) { CMSInfo4Category::s_pNfo4DataSource->UpdateCurrentControl(p4Ctrl); p4Ctrl->ShowWindow(SW_SHOW); p4Ctrl->SetMSInfoView(this->m_dwView); p4Ctrl->MSInfoUpdateView(); p4Ctrl->MSInfoRefresh(); } else p4Ctrl->ShowWindow(SW_HIDE); } catch (CException* pException) { ASSERT(0); pException->Delete(); } catch (...) { ASSERT(0); } return S_OK; } //TD: Move into nfodata.cpp? CNFO4DataSource::CNFO4DataSource() { m_pCurrentControl = NULL; } CNFO4DataSource::~CNFO4DataSource() { CString strKey; CMSIControl* pCtrl; // m_pCurrentControl->DestroyWindow(); for(POSITION mapPos = m_mapCLSIDToControl.GetStartPosition( );;) { if (!mapPos) { break; } m_mapCLSIDToControl.GetNextAssoc(mapPos, strKey, (void*&)pCtrl); pCtrl->DestroyWindow(); delete pCtrl; } } void CNFO4DataSource::UpdateCurrentControl(CMSIControl* pControl) { if (m_pCurrentControl && pControl != m_pCurrentControl) { m_pCurrentControl->ShowWindow(SW_HIDE); } m_pCurrentControl = pControl; } //--------------------------------------------------------------------------- // Creates the datasource, and the root CMSInfo4Category //--------------------------------------------------------------------------- HRESULT CNFO4DataSource::Create(CString strFileName) { CMSInfo4Category * pNewRoot = NULL; CMSInfo4Category::SetDataSource(this); HRESULT hr = CMSInfo4Category::ReadMSI4NFO(strFileName, &pNewRoot); if (SUCCEEDED(hr) && pNewRoot) m_pRoot = pNewRoot; return hr; } void CMSInfo4Category::Print(CMSInfoPrintHelper* pPrintHelper, BOOL bRecursive) { #ifdef A_STEPHL // ASSERT(0); #endif 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; //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); }*/ // When allocating the // buffer for the information, allocate 5 extra bytes (1 for the null, and // 4 to hold "\r\n\r\n"). CString strLine; CMSIControl* pControl = NULL; if (!CMSInfo4Category::s_pNfo4DataSource->GetControlFromCLSID(m_strCLSID,pControl)) { //need to make sure it's not "empty parent" category, like HARDWARE RESOURCES if ("{00000000-0000-0000-0000-000000000000}" == m_strCLSID) { //this is a parent node, with no data of its own CString strCatHeading; strCatHeading.LoadString(IDS_CATEGORYHEADING); pPrintHelper->PrintLine(strCatHeading); } else { pPrintHelper->PrintLine(""); strLine.LoadString(IDS_NOOCX); pPrintHelper->PrintLine(strLine); CString strDetail; strDetail.LoadString(IDS_NOOCXDETAIL); pPrintHelper->PrintLine(strDetail); } } else { //pControl->SetMSInfoView(this->m_dwView); //pControl->MSInfoUpdateView(); //pControl->MSInfoRefresh(); long lBufferLength = pControl->MSInfoGetData(m_dwView, NULL, 0); if (lBufferLength < 0) { ASSERT(1); } else { char * pBuffer = new char[lBufferLength + 5]; if (pBuffer) { strcpy(pBuffer, "\r\n\r\n"); if (!pControl->MSInfoGetData(m_dwView, (long *) (&pBuffer[4]), lBufferLength + 1) == lBufferLength) { ASSERT(1); } else { //process pBuffer for strings to print CString strBuff(pBuffer); CString strCharSet = _T("\r\n"); strCharSet += _T("\r"); //strCharSet += 10; strCharSet += _T("\n"); //strCharSet += 13; /*for(int i = 0; ;) { i = strBuff.FindOneOf(strCharSet); strLine = strBuff.Left(i); pPrintHelper->PrintLine(strLine); i+=2; strBuff = strBuff.Right(strBuff.GetLength() - i); //a-stephl: to fix OSR4.1 bug#135918 if (i > strBuff.GetLength()) { pPrintHelper->PrintLine(strBuff); break; } }*/ //a-stephl: to fix OSR4.1 bug#135918 //for(int i = 0; ;) int i = 0; while( i > 0) { i = strBuff.FindOneOf(strCharSet); if (-1 == i) { pPrintHelper->PrintLine(strBuff); } strLine = strBuff.Left(i); pPrintHelper->PrintLine(strLine); i+=2; strBuff = strBuff.Right(strBuff.GetLength() - i); } //end a-stephl: to fix OSR4.1 bug#135918 delete pBuffer; } } } } if (bRecursive && this->m_pFirstChild != NULL) { for(CMSInfo4Category* pChild = (CMSInfo4Category*) this->GetFirstChild();pChild != NULL;pChild = (CMSInfo4Category*) pChild->GetNextSibling()) { pChild->Print(pPrintHelper,TRUE); } } } void CMSInfo4Category::Print(HDC hDC, BOOL bRecursive,int nStartPage, int nEndPage) { //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); CTime tNow = CTime::GetCurrentTime(); CString strTimeFormat; strTimeFormat.LoadString(IDS_TIME_FORMAT); CString strHeaderText = tNow.Format(strTimeFormat); pPrintHelper->PrintLine(strHeaderText); pPrintHelper->PrintLine(""); Print(pPrintHelper,bRecursive); delete pPrintHelper; } HRESULT CMSInfo4Category::RefreshAllForPrint(HWND hWnd, CRect rctList) { if (this->m_pFirstChild != NULL) { for(CMSInfo4Category* pChild = (CMSInfo4Category*) this->GetFirstChild();pChild != NULL;pChild = (CMSInfo4Category*) pChild->GetNextSibling()) { CMSIControl* p4Ctrl; //if (pChild->GetClsid() != if (!CMSInfo4Category::s_pNfo4DataSource->GetControlFromCLSID(pChild->m_strCLSID,p4Ctrl)) { if (FAILED(pChild->CreateControl(hWnd,rctList))) { return E_FAIL; } if (CMSInfo4Category::s_pNfo4DataSource->GetControlFromCLSID(pChild->m_strCLSID,p4Ctrl)) { p4Ctrl->ShowWindow(SW_HIDE); } else //if (!CMSInfo4Category::s_pNfo4DataSource->GetControlFromCLSID(pChild->m_strCLSID,p4Ctrl)) { //ASSERT(!pChild->IsDisplayableCategory() && "Invalid Class ID"); //OCX is not installed on this system } } pChild->RefreshAllForPrint(hWnd,rctList); } } return S_OK; } BOOL CMSInfo4Category::IsDisplayableCategory() { if ("{00000000-0000-0000-0000-000000000000}" == this->m_strCLSID) { return FALSE; } return TRUE; } //----------------------------------------------------------------------------- //Saves category information as text, recursing children in bRecursive is true //----------------------------------------------------------------------------- BOOL CMSInfo4Category::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\r\n"); pTxtFile->WriteString(strOut); CMSIControl* pControl = NULL; if (!CMSInfo4Category::s_pNfo4DataSource->GetControlFromCLSID(m_strCLSID,pControl)) { ASSERT(1); } else { pControl->MSInfoUpdateView(); pControl->MSInfoRefresh(); long lBufferLength = pControl->MSInfoGetData(m_dwView, NULL, 0); if (lBufferLength < 0) { ASSERT(1); } else { char * pBuffer = new char[lBufferLength + 5]; if (pBuffer) { strcpy(pBuffer, "\r\n\r\n"); if (!pControl->MSInfoGetData(m_dwView, (long *) (&pBuffer[4]), lBufferLength + 1) == lBufferLength) { ASSERT(0 && "could not get data from control"); } else { //process pBuffer for strings to print CString strBuff(pBuffer); pTxtFile->WriteString(pBuffer); delete pBuffer; } } } } if (bRecursive && this->m_pFirstChild != NULL) { for(CMSInfo4Category* pChild = (CMSInfo4Category*) this->GetFirstChild();pChild != NULL;pChild = (CMSInfo4Category*) pChild->GetNextSibling()) { pChild->SaveAsText(pTxtFile,TRUE); } } return TRUE; }