/***************************************************************************\ * * 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(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; }