448 lines
13 KiB
C++
448 lines
13 KiB
C++
|
// 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<COCXFolder *>(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<COCXFolder *>(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<CBufferV410DataSource *>(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<void **>(&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<void **>(&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;
|
||
|
};
|