Windows2003-3790/windows/advcore/duser/engine/msg/msgclass.cpp
2020-09-30 16:53:55 +02:00

617 lines
18 KiB
C++

/***************************************************************************\
*
* File: MsgClass.h
*
* Description:
* MsgClass.h implements the "Message Class" object that is created for each
* different message object type. Each object has a corresponding MsgClass
* that provides information about that object type.
*
*
* History:
* 8/05/2000: JStall: Created
*
* Copyright (C) 2000 by Microsoft Corporation. All rights reserved.
*
\***************************************************************************/
#include "stdafx.h"
#include "Msg.h"
#include "MsgClass.h"
#include "MsgTable.h"
#include "MsgObject.h"
#include "ClassLibrary.h"
/***************************************************************************\
*****************************************************************************
*
* class MsgClass
*
*****************************************************************************
\***************************************************************************/
/***************************************************************************\
*
* MsgClass::~MsgClass
*
* ~MsgClass() cleans up resources associated with a specific message class.
*
\***************************************************************************/
MsgClass::~MsgClass()
{
//
// Clean up internal resources
//
if (m_pmt != NULL) {
m_pmt->Destroy();
}
DeleteAtom(m_atomName);
}
/***************************************************************************\
*
* MsgClass::Build
*
* Build() creates a placeholder MsgClass so that we can register Stubs to be
* setup after the implementation is registered.
*
* NOTE: We can NOT instantiate MsgObject's until the implementation has also
* been registered.
*
\***************************************************************************/
HRESULT
MsgClass::Build(
IN LPCWSTR pszClassName, // Class information
OUT MsgClass ** ppmcNew) // New MsgClass
{
//
// Allocate the new MsgClass
//
ATOM atomName = AddAtomW(pszClassName);
if (atomName == 0) {
return E_OUTOFMEMORY;
}
MsgClass * pmcNew = ProcessNew(MsgClass);
if (pmcNew == NULL) {
return E_OUTOFMEMORY;
}
pmcNew->m_pszName = pszClassName;
pmcNew->m_atomName = atomName;
*ppmcNew = pmcNew;
return S_OK;
}
/***************************************************************************\
*
* MsgClass::xwDeleteHandle
*
* xwDeleteHandle() is called when the application calls ::DeleteHandle() on
* an object.
*
\***************************************************************************/
BOOL
MsgClass::xwDeleteHandle()
{
//
// MsgClass's can not be deleted externally. Once registered, they exist
// for the lifetime of the process. The reason is that we don't keep track
// of how many already created objects may be using the MsgTable owned by
// the MsgClass. We also would need to ensure that no classes derive from
// this class.
//
return TRUE;
}
/***************************************************************************\
*
* MsgClass::RegisterGuts
*
* RegisterGuts() is called by the implementation class to provide the "guts"
* of a MsgClass. This fills in the outstanding information required to be
* able to actually instantiate the MsgClass.
*
\***************************************************************************/
HRESULT
MsgClass::RegisterGuts(
IN OUT DUser::MessageClassGuts * pmcInfo)
// Class information
{
AssertWritePtr(pmcInfo);
if (IsGutsRegistered()) {
PromptInvalid("The implementation has already been registered");
return DU_E_CLASSALREADYREGISTERED;
}
//
// Find the super
//
const MsgClass * pmcSuper = NULL;
if ((pmcInfo->pszSuperName != NULL) && (pmcInfo->pszSuperName[0] != '\0')) {
pmcSuper = GetClassLibrary()->FindClass(FindAtomW(pmcInfo->pszSuperName));
if (pmcSuper == NULL) {
PromptInvalid("The specified super class was not found");
return DU_E_NOTFOUND;
}
// TODO: Remove this requirement that the Super's guts must be
// registered before this class's guts can be registered.
if (!pmcSuper->IsGutsRegistered()) {
PromptInvalid("The super class's implementation to be registered first");
return DU_E_CLASSNOTIMPLEMENTED;
}
}
m_pmcSuper = pmcSuper;
m_nVersion = pmcInfo->nClassVersion;
m_pfnPromote = pmcInfo->pfnPromote;
m_pfnDemote = pmcInfo->pfnDemote;
//
// Build the MsgTable for the new MsgClass
//
MsgTable * pmtNew;
HRESULT hr = MsgTable::Build(pmcInfo, this, &pmtNew);
if (FAILED(hr)) {
return hr;
}
m_pmt = pmtNew;
//
// Return out the new MsgClass and the Super. These are used by the
// caller to create instances of the object.
//
pmcInfo->hclNew = GetHandle();
pmcInfo->hclSuper = pmcSuper != NULL ? pmcSuper->GetHandle() : NULL;
//
// Now that the Guts are registered, we can backfill all of the Stubs
// and Supers. After this, we no longer need to store them.
//
int idx;
int cStubs = m_arStubs.GetSize();
for (idx = 0; idx < cStubs; idx++) {
FillStub(m_arStubs[idx]);
}
m_arStubs.RemoveAll();
int cSupers = m_arSupers.GetSize();
for (idx = 0; idx < cSupers; idx++) {
FillSuper(m_arSupers[idx]);
}
m_arSupers.RemoveAll();
return S_OK;
}
/***************************************************************************\
*
* MsgClass::RegisterStub
*
* RegisterStub() starts the lookup process for a Stub. If the class is
* already setup, we can fill in immediately. If the class isn't already
* setup, we need to wait until it is setup and then we call post-fill-in.
*
\***************************************************************************/
HRESULT
MsgClass::RegisterStub(
IN OUT DUser::MessageClassStub * pmcInfo) // Stub information to be filled in
{
//
// NOTE: ONLY fill in the Stub if the caller is requesting the messages to
// be filled in. If cMsgs == 0, they are probably just preregistering the
// class (for a Super) and have allocated pmcInfo on the stack. In this
// case, it is very important not to backfill it since we will trash the
// memory.
//
if (pmcInfo->cMsgs > 0) {
if (IsGutsRegistered()) {
return FillStub(pmcInfo);
} else {
return m_arStubs.Add(pmcInfo) >= 0 ? S_OK : E_OUTOFMEMORY;
}
}
return S_OK;
}
/***************************************************************************\
*
* MsgClass::RegisterSuper
*
* RegisterSuper() starts the lookup process for a Super. If the class is
* already setup, we can fill in immediately. If the class isn't already
* setup, we need to wait until it is setup and then we call post-fill-in.
*
\***************************************************************************/
HRESULT
MsgClass::RegisterSuper(
IN OUT DUser::MessageClassSuper * pmcInfo) // Stub information to be filled in
{
if (IsGutsRegistered()) {
FillSuper(pmcInfo);
return S_OK;
} else {
return m_arSupers.Add(pmcInfo) >= 0 ? S_OK : E_OUTOFMEMORY;
}
}
/***************************************************************************\
*
* MsgClass::xwConstructCB
*
* xwConstructCB() is called back by a pfnPromoteClass function to
* initialize a super class's portion of a MsgObject. This allows the
* specific pfnPromoteClass to decide what implementation classes to actually
* implement and which to delegate.
*
* The caller passes in the super-class to actually construct.
*
\***************************************************************************/
HRESULT CALLBACK
MsgClass::xwConstructCB(
IN DUser::Gadget::ConstructCommand cmd, // Construction code
IN HCLASS hclCur, // Class being initialized
IN DUser::Gadget * pgad, // Object being initialized
IN void * pvData) // Construction information
{
//
// Validate parameters.
// NOTE: We NEED to check if hclSuper == NULL when passed in since this is
// LEGAL (if we don't need a super-class generated).
// ValidateMsgClass() will set pmcSuper == NULL if there is a
// validation error.
//
if (hclCur == NULL) {
return S_OK;
}
const MsgClass * pmcCur = ValidateMsgClass(hclCur);
if (pmcCur == NULL) {
PromptInvalid("Given invalid HCLASS during xwConstructSuperCB()");
return E_INVALIDARG;
}
MsgObject * pmoNew = MsgObject::CastMsgObject(pgad);
if (pmoNew == NULL) {
return E_INVALIDARG;
}
HRESULT hr;
switch (cmd)
{
case DUser::Gadget::ccSuper:
//
// pmcCur specifies the super-class that is being asked to build its
// object.
//
hr = pmcCur->xwBuildUpObject(pmoNew, reinterpret_cast<DUser::Gadget::ConstructInfo *>(pvData));
break;
case DUser::Gadget::ccSetThis:
//
// pmcCur specifies the class to start filling the this pointers.
//
{
int idxStartDepth = pmoNew->GetBuildDepth();
int idxEndDepth = pmcCur->m_pmt->GetDepth();
pmoNew->FillThis(idxStartDepth, idxEndDepth, pvData, pmcCur->m_pmt);
hr = S_OK;
}
break;
default:
PromptInvalid("Unknown dwCode to ConstructProc()");
hr = E_INVALIDARG;
}
return hr;
}
/***************************************************************************\
*
* MsgClass::xwBuildUpObject
*
* xwBuildUpObject() builds up an newly constructed MsgObject by calling
* the most-derived Promotion function to initialize the MsgObject. This
* Promotion function will usually callback to xwConstructCB() to
* initialize base classes that are not provided in that implementation.
*
* As each Promotion function is called, xwBuildUpObject() will be called
* from xwConstructCB() to construct the super-classes. As each Super
* finishes construction, the "this array" on the MsgObject is updated.
*
\***************************************************************************/
HRESULT
MsgClass::xwBuildUpObject(
IN MsgObject * pmoNew, // Object being constructed / promoted
IN DUser::Gadget::ConstructInfo * pciData // Construction information
) const
{
//
// For non-internally implemented classes, we need to callback to get the
// "this" pointer to use. The callback is responsible for calling
// ConstructProc(CONSTRUCT_SETTHIS) to Promote the object and set the
// "this" pointers. For internally implemented classes, we use the
// MsgObject to directly Promote the object.
//
//
HRESULT hr;
if (IsInternal()) {
hr = S_OK;
} else {
//
// Callback to this Super to give a chance to construct. This is done
// from the most derived class and relies on callbacks to initialize
// super-classes.
//
DUser::Gadget * pgad
= pmoNew->GetGadget();
HCLASS hcl = GetHandle();
hr = (m_pfnPromote)(xwConstructCB, hcl, pgad, pciData);
}
#if DBG
if (SUCCEEDED(hr)) {
//
// Check that the Promotion function properly set the this pointer.
//
int idxObjectDepth = pmoNew->GetDepth();
int idxSuperDepth = m_pmt->GetDepth();
if (idxObjectDepth <= idxSuperDepth) {
PromptInvalid("The PromoteProc() function did not call ConstructProc(CONSTRUCT_SETTHIS).");
}
if (pmoNew->GetThis(idxSuperDepth) == ULongToPtr(0xA0E20000 + idxSuperDepth)) {
PromptInvalid("The PromoteProc() function did not call ConstructProc(CONSTRUCT_SETTHIS).");
}
}
#endif // DBG
return hr;
}
/***************************************************************************\
*
* MsgClass::xwBuildObject
*
* xwBuildObject() builds and initializes a new MsgObject.
*
\***************************************************************************/
HRESULT
MsgClass::xwBuildObject(
OUT MsgObject ** ppmoNew, // New MsgObject
IN DUser::Gadget::ConstructInfo * pciData // Construction information
) const
{
//
// Allocate a new object:
// 1. Walk up the inheritance chain to determine the DUser object to build
// that will provide MsgObject functionality.
// 2. While walking up, Verify that the guts of all classes have been
// properly registered.
// 3. Kick off the build-up process.
//
HRESULT hr;
MsgObject * pmoNew = NULL;
const MsgClass * pmcInternal = this;
while (pmcInternal != NULL) {
if (!pmcInternal->IsGutsRegistered()) {
PromptInvalid("The implementation of the specified class has not been provided");
return DU_E_CLASSNOTIMPLEMENTED;
}
if (pmcInternal->IsInternal()) {
AssertMsg(pmoNew == NULL, "Must be NULL for internal Promote() functions");
hr = (pmcInternal->m_pfnPromote)(NULL, pmcInternal->GetHandle(), (DUser::Gadget *) &pmoNew, pciData);
if (FAILED(hr)) {
return E_OUTOFMEMORY;
}
AssertMsg(pmoNew != NULL, "Internal objects must fill in the MsgObject");
AssertMsg(pmoNew->GetHandleType() != htNone, "Must have a valid handle type");
break;
}
pmcInternal = pmcInternal->GetSuper();
}
if (pmoNew == NULL) {
AssertMsg(pmcInternal == NULL, "Internal classes must have already allocated the MsgObject");
pmoNew = ClientNew(MsgObject);
if (pmoNew == NULL) {
return E_OUTOFMEMORY;
}
}
hr = pmoNew->PreAllocThis(m_pmt->GetDepth() + 1);
if (FAILED(hr)) {
goto Error;
}
if (pmcInternal != NULL) {
int cObjectDepth = pmcInternal->m_pmt->GetDepth();
pmoNew->FillThis(0, cObjectDepth, pmoNew, pmcInternal->m_pmt);
}
hr = xwBuildUpObject(pmoNew, pciData);
if (FAILED(hr)) {
goto Error;
}
*ppmoNew = pmoNew;
return S_OK;
Error:
if (pmoNew != NULL) {
xwTearDownObject(pmoNew);
}
return hr;
}
/***************************************************************************\
*
* MsgClass::xwTearDownObject
*
* xwTearDownObject() tears down a MsgObject as part of the destruction
* process. This gives each of the implentation classes an opportunity to
* cleanup resources, similar to having a destructor in C++.
*
\***************************************************************************/
void
MsgClass::xwTearDownObject(
IN MsgObject * pmoNew // Object being destroyed.
) const
{
DUser::Gadget * pgad
= pmoNew->GetGadget();
int idxThis = m_pmt->GetDepth();
const MsgClass * pmcCur = this;
const MsgClass * pmcNext, * pmcTest;
while (pmcCur != NULL) {
HCLASS hcl = pmcCur->GetHandle();
void * pvThis = pmoNew->GetThis(idxThis);
hcl = (pmcCur->m_pfnDemote)(hcl, pgad, pvThis);
//
// Determine how many class to remove by how far up the chain this
// MsgClass is.
// - TODO: Need to check that returned class is actually in the chain.
//
pmcNext = ValidateMsgClass(hcl);
if ((hcl != NULL) && (pmcNext == NULL)) {
PromptInvalid("Incorrect HCLASS returned from Demote function. Object will not be properly destroyed.");
}
pmcTest = pmcCur;
int cDepth = 0;
while ((pmcTest != NULL) && (pmcTest != pmcNext)) {
cDepth++;
pmcTest = pmcTest->m_pmcSuper;
}
pmoNew->Demote(cDepth);
idxThis -= cDepth;
pmcCur = pmcNext;
}
}
/***************************************************************************\
*
* MsgClass::FillStub
*
* FillStub() provides the calling stub with information about a previously
* registered MsgClass.
*
\***************************************************************************/
HRESULT
MsgClass::FillStub(
IN OUT DUser::MessageClassStub * pmcInfo // Stub information to be filled in
) const
{
HRESULT hr = S_OK;
ATOM atomMsg;
int idxSlot;
int cbBeginOffset = sizeof(MsgTable);
DUser::MessageInfoStub * rgMsgInfo = pmcInfo->rgMsgInfo;
int cMsgs = pmcInfo->cMsgs;
for (int idx = 0; idx < cMsgs; idx++) {
if (((atomMsg = FindAtomW(rgMsgInfo[idx].pszMsgName)) == 0) ||
((idxSlot = m_pmt->FindIndex(atomMsg)) < 0)) {
//
// Could not find the function, so store a -1 to signal that this
// slot with the error.
//
PromptInvalid("Unable to find message during lookup");
hr = DU_E_MESSAGENOTFOUND;
rgMsgInfo[idx].cbSlotOffset = -1;
} else {
//
// Successfully found the function. We need to store the offset
// to the slot so that it can be directly accessed without any math
// or special knowledge of our internals.
//
int cbSlotOffset = idxSlot * sizeof(MsgSlot) + cbBeginOffset;
rgMsgInfo[idx].cbSlotOffset = cbSlotOffset;
}
}
return hr;
}
/***************************************************************************\
*
* MsgClass::FillSuper
*
* FillStub() provides the calling super with information about a previously
* registered MsgClass.
*
\***************************************************************************/
void
MsgClass::FillSuper(
IN OUT DUser::MessageClassSuper * pmcInfo // Super information to be filled in
) const
{
pmcInfo->pmt = m_pmt;
}