// V410File.cpp Implementation of Version 4.10 data file methods. // // Copyright (c) 1998-1999 Microsoft Corporation #include "DataSrc.h" #ifndef IDS_V410FILENODE #include "resource.h" #endif #include "resrc1.h" //----------------------------------------------------------------------------- // This struct is used to read the internals of a 4.10 data file. //----------------------------------------------------------------------------- typedef struct { char szCLSID[40]; char szStreamName[12]; char szName[_MAX_PATH]; char szVersion[20]; DWORD dwSize; } SAVED_CONTROL_INFO; /* * CBufferV410DataSource - Stub * * History: a-jsari 9/17/97 Initial version */ CBufferV410DataSource::CBufferV410DataSource(CMSInfoFile *pFileSink) :CBufferDataSource(pFileSink) { } //----------------------------------------------------------------------------- // The constructor for the version 4.10 NFO file data source reads information // from an IStream in a compound file. //----------------------------------------------------------------------------- CBufferV410DataSource::CBufferV410DataSource(IStorage * pStorage, IStream * pStream) : CBufferDataSource(NULL) { ReadMSInfo410Stream(pStream); m_pStorage = pStorage; m_pStorage->AddRef(); } //----------------------------------------------------------------------------- // The destructor should release the pointer to the compound doc we saved. //----------------------------------------------------------------------------- CBufferV410DataSource::~CBufferV410DataSource() { if (m_pStorage) { m_pStorage->Release(); m_pStorage = NULL; } } //----------------------------------------------------------------------------- // Read in the information from the "msinfo" stream. The most vital things // in this file are the tree structure of categories, and the map from // CLSID to stream name. //----------------------------------------------------------------------------- BOOL CBufferV410DataSource::ReadMSInfo410Stream(IStream *pStream) { const DWORD MSI_FILE_VER = 0x03000000; DWORD dwVersion, dwCount; // First, read and check the version number in the stream. if (FAILED(pStream->Read((void *) &dwVersion, sizeof(DWORD), &dwCount)) || dwCount != sizeof(DWORD)) return FALSE; if (dwVersion != MSI_FILE_VER) return FALSE; // 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; if (FAILED(pStream->Read((void *) &dwSize, sizeof(DWORD), &dwCount)) || dwCount != sizeof(DWORD)) return FALSE; char * szBuffer = new char[dwSize]; if (szBuffer == NULL) return FALSE; if (FAILED(pStream->Read((void *) szBuffer, dwSize, &dwCount)) || (int)dwCount != dwSize) { delete szBuffer; return FALSE; } // We don't actually care about these values (now at least). #if FALSE 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"); #endif 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; if (FAILED(pStream->Read((void *) &dwControlCount, sizeof(DWORD), &dwCount)) || dwCount != sizeof(int)) return FALSE; SAVED_CONTROL_INFO controlInfo; CString strCLSID, strStreamName; for (DWORD i = 0; i < dwControlCount; i++) { if (FAILED(pStream->Read((void *) &controlInfo, sizeof(SAVED_CONTROL_INFO), &dwCount)) || dwCount != sizeof(SAVED_CONTROL_INFO)) return FALSE; strCLSID = controlInfo.szCLSID; strStreamName = controlInfo.szStreamName; // We don't currently care about this information... #if FALSE strSize.Format("%ld", controlInfo.dwSize); strInfo.FormatMessage(IDS_OCX_INFO, controlInfo.szName, controlInfo.szVersion, strSize); m_mapCLSIDToInfo.SetAt(strCLSID, strInfo); #endif m_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 FALSE; 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 FALSE; if (!RecurseLoad410Tree(pStream)) return FALSE; // 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 FALSE; } else return FALSE; return TRUE; } //----------------------------------------------------------------------------- // This function creates a COCXFolder object based on the information read // from the stream. //----------------------------------------------------------------------------- COCXFolder * CBufferV410DataSource::ReadOCXFolder(IStream *pStream, COCXFolder * pParent, COCXFolder * pPrevious) { // 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 NULL; if (FAILED(pStream->Read((void *) &fControl, sizeof(BOOL), NULL))) return NULL; if (FAILED(pStream->Read((void *) &dwView, sizeof(DWORD), NULL))) return NULL; if (FAILED(pStream->Read((void *) &clsidCategory, sizeof(CLSID), NULL))) return NULL; if (FAILED(pStream->Read((void *) &szName, sizeof(char) * 100, NULL))) return NULL; USES_CONVERSION; LPOLESTR lpName = A2W(szName); COCXFolder * pFolder = new COCXFolder(this, clsidCategory, pParent, pPrevious, dwView, lpName); return pFolder; } //----------------------------------------------------------------------------- // 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. //----------------------------------------------------------------------------- BOOL CBufferV410DataSource::RecurseLoad410Tree(IStream *pStream) { m_RootFolder = NULL; // 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. COCXFolder * aFolderHistory[20]; for (int i = 0; i < 20; i++) aFolderHistory[i] = 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 = 0; if (FAILED(pStream->Read((void *) &iLevel, sizeof(int), &dwCount)) || dwCount != sizeof(int)) return FALSE; int iLastLevel = iLevel; while (iLevel >= 0 && iLevel < 20) { aFolderHistory[iLevel] = ReadOCXFolder(pStream, ((iLevel) ? aFolderHistory[iLevel - 1] : NULL), ((iLevel <= iLastLevel) ? aFolderHistory[iLevel] : NULL)); iLastLevel = iLevel; if (FAILED(pStream->Read((void *) &iLevel, sizeof(int), &dwCount)) || dwCount != sizeof(int)) return FALSE; } m_RootFolder = aFolderHistory[0]; // The root OCX folder will not show up in version 5.0, so we need to make // the first child folder contain the same values as the root. COCXFolder * pRootFolder = reinterpret_cast(m_RootFolder); if (pRootFolder) { CFolder * pOriginalChild = pRootFolder->m_ChildFolder; pRootFolder->m_ChildFolder = new COCXFolder(this, pRootFolder->m_clsid, pRootFolder, NULL, pRootFolder->m_dwView, L""); reinterpret_cast(pRootFolder->m_ChildFolder)->m_strName.LoadString(IDS_410SUMMARY_NODE); pRootFolder->m_ChildFolder->m_NextFolder = pOriginalChild; } // 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 FALSE; } return TRUE; } /* * GetNodeName - * * History: a-jsari 1/16/98 Initial version. */ BOOL CBufferV410DataSource::GetNodeName(CString &strNodeName) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); strNodeName.Format(IDS_V410FILENODE); return TRUE; } /* * GetRootNode - Stub * * History: a-jsari 9/17/97 Initial version */ CFolder *CBufferV410DataSource::GetRootNode() { return CDataSource::GetRootNode(); } /* * ReadHeader - Stub * * History: a-jsari 9/17/97 Initial version */ void CBufferV410DataSource::ReadHeader(CMSInfoFile *) { } /* * ReadFolder - Stub * * History: a-jsari 9/17/97 Initial version */ void CBufferV410DataSource::ReadFolder(CMSInfoFile *, CFolder * & /* pParentFolder */, CFolder * /* pFolder */) { } /* * Save - Stub * * History: a-jsari 11/13/97 Initial version */ HRESULT CBufferV410DataSource::Save(IStream * /* pStm */) { return E_NOTIMPL; } /* * Find - Stub * * History: a-jsari 12/11/97 Initial version */ BOOL CBufferV410DataSource::Find(const CString & /* strSearch */, long /* lFindOptions */) { return FALSE; } //----------------------------------------------------------------------------- // When the OCX folder is refreshed, we should call the appropriate methods // in the OCX (if it is there). //----------------------------------------------------------------------------- BOOL COCXFolder::Refresh(IUnknown * pUnknown) { // Get the CLSID as a string (since all our tables are based on the string). LPOLESTR lpCLSID; if (FAILED(StringFromCLSID(m_clsid, &lpCLSID))) return FALSE; CString strCLSID(lpCLSID); CBufferV410DataSource * pV410Source = reinterpret_cast(DataSource()); if (pV410Source == NULL) return FALSE; if (pUnknown == NULL) return FALSE; // Get the stream for this control, and load it. CString strStream; if (pV410Source->m_pStorage && pV410Source->m_mapStreams.Lookup(strCLSID, strStream)) { DWORD grfMode = STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE; IStream * pStream = NULL; if (SUCCEEDED(pV410Source->m_pStorage->OpenStream(strStream, NULL, grfMode, 0, &pStream))) { IPersistStreamInit * pPersistStream = NULL; if (SUCCEEDED(pUnknown->QueryInterface(IID_IPersistStreamInit, reinterpret_cast(&pPersistStream))) && pPersistStream) { if (SUCCEEDED(pPersistStream->Load(pStream))) { // Delete the entry for the this stream (so that we don't keep // loading the stream into the control). pV410Source->m_mapStreams.RemoveKey(strCLSID); } pPersistStream->Release(); } pStream->Release(); } } // Set the view index for the OCX. SetOCXView(pUnknown, m_dwView); return TRUE; } //--------------------------------------------------------------------------- // Use the OCX's IDispatch interface to set the view index and call the // function to refresh the control (for the new index). //--------------------------------------------------------------------------- void COCXFolder::SetOCXView(IUnknown * pUnknown, DWORD dwView) { IDispatch * pDispatch = NULL; if (SUCCEEDED(pUnknown->QueryInterface(IID_IDispatch, reinterpret_cast(&pDispatch))) && pDispatch) { DISPID viewdispid, updatedispid; if (!GetDISPID(pDispatch, _T("MSInfoView"), &viewdispid) || !GetDISPID(pDispatch, _T("MSInfoUpdateView"), &updatedispid)) return; DISPID mydispid = DISPID_PROPERTYPUT; DISPPARAMS dispparams; VARIANTARG disparg; disparg.vt = VT_I4; disparg.lVal = m_dwView; dispparams.rgvarg = &disparg; dispparams.rgdispidNamedArgs = &mydispid; dispparams.cArgs = 1; dispparams.cNamedArgs = 1; pDispatch->Invoke(viewdispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0}; pDispatch->Invoke(updatedispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparamsNoArgs, NULL, NULL, NULL); pDispatch->Release(); } } //--------------------------------------------------------------------------- // GetDISPID returns the DISPID for a given string, by looking it up using // IDispatch->GetIDsOfNames. This avoids hardcoding DISPIDs in this class. //--------------------------------------------------------------------------- BOOL COCXFolder::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; } //----------------------------------------------------------------------------- // Return the folder's CLSID as string. //----------------------------------------------------------------------------- BOOL COCXFolder::GetCLSIDString(CString & strCLSID) { LPOLESTR lpCLSID; if (FAILED(StringFromCLSID(m_clsid, &lpCLSID))) return FALSE; strCLSID = lpCLSID; CoTaskMemFree(lpCLSID); return TRUE; };