Figure 3   Simple Dynamic Object and View

OnObjectInfo Method of CSimpleView

BEGIN_MESSAGE_MAP(CSimpleView, CNSFlexFormView)
    //{{AFX_MSG_MAP(CSimpleView)
        // NOTE - the ClassWizard will add and remove 
        //        mapping macros here.
    //}}AFX_MSG_MAP
    ON_MESSAGE(WM_OBJECTINFO,OnObjectInfo)
END_MESSAGE_MAP()

LRESULT CSimpleView::OnObjectInfo(WPARAM wParam, LPARAM lParam)
{
    ObjectInfoMessage* pObjectInfoMessage =
        (ObjectInfoMessage*)(lParam);

    CObjectBroker* pObjectBroker = pObjectInfoMessage->pObjectBroker;

    CObjectInfo* pObjectInfo = 
        pObjectBroker->GetObjectInfoByKey(pObjectInfoMessage->
                                          pszObjectKey);
    ASSERT(pObjectInfo);

    m_pSimpleObject = (CSimpleObject*)pObjectInfo->GetObject();
    ASSERT(m_pSimpleObject);
    ASSERT(m_pSimpleObject->IsKindOf(RUNTIME_CLASS(CSimpleObject)));

    return 1;
}
DllMain Function for Simple.dll
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        TRACE0("SIMPLE.DLL Initializing!\n");
        
        // Extension DLL one-time initialization
        AfxInitExtensionModule(SimpleDLL, hInstance);

        // Insert this DLL into the resource chain
        new CDynLinkLibrary(SimpleDLL);
    }
    else if (dwReason == DLL_PROCESS_DETACH)
    {
        TRACE0("SIMPLE.DLL Terminating!\n");

        AfxTermExtensionModule(SimpleDLL);
    }
    return 1;   // ok
}
ClassInfo Function Exported from Simple.dll
extern "C" void WINAPI ClassInfo(CLSID& clsidClassID, 
                                 CLSID& clsidClassCategory, 
                                 CString& csDescription,
                                 CRuntimeClass*& pObjectClass, 
                                 CRuntimeClass*& pFrameClass,
                                 CRuntimeClass*& pViewClass)
{
    // ID: {34E338C1-86E1-11d0-8984-00008609452B}
    CLSID clsidID = { 0x34e338c1, 0x86e1, 0x11d0, 
        { 0x89, 0x84, 0x0, 0x0, 0x86, 0x9, 0x45, 0x2b } };

    // CATEGORY: {8CEDC521-90AF-11d0-A263-2AC81B000000}
    CLSID clsidCategory = { 0x8cedc521, 0x90af, 0x11d0,
        { 0xa2, 0x63, 0x2a, 0xc8, 0x1b, 0x0, 0x0, 0x0 } };

    clsidClassID = clsidID;
    clsidClassCategory = clsidCategory;

    csDescription = "Simple Object";

    pObjectClass = RUNTIME_CLASS(CSimpleObject);
    pFrameClass = RUNTIME_CLASS(CFlexibleChildFrame);
    pViewClass = RUNTIME_CLASS(CSimpleView);
}


Figure 8   LoadDllModules Method of CclassBroker


 void CClassBroker::LoadDLLModules()
 {
     char szBuffer[MAX_PATH], szDrive[3], szPath[MAX_PATH];
     GetModuleFileName(AfxGetInstanceHandle(),szBuffer,MAX_PATH);
     _splitpath(szBuffer,szDrive,szPath,NULL,NULL);
     CString csPath = szDrive;
     csPath += szPath;
     CString csDLLSearch = csPath + "*.dll";
     WIN32_FIND_DATA FileData;
     HANDLE hFind;
 
     for (BOOL bOK = ((hFind = FindFirstFile(csDLLSearch,&FileData))
         != INVALID_HANDLE_VALUE); bOK;
         bOK = FindNextFile(hFind,&FileData))
     {
         CString csDLL = csPath;
         csDLL += FileData.cFileName;
 
         HMODULE hModule = AfxLoadLibrary(csDLL);
         ASSERT(hModule);
 
         typedef void (WINAPI* CLASSINFO)(CLSID&,CLSID&,CString&, 
                       CRuntimeClass*&,CRuntimeClass*&,CRuntimeClass*&);
 
         CLASSINFO pClassInfo =
             (CLASSINFO)GetProcAddress(hModule,"ClassInfo");
 
         if (pClassInfo)
         {
             CLSID clsidClassID;
             CLSID clsidClassCategory;
             CString csDescription;
             CRuntimeClass* pObjectClass;
             CRuntimeClass* pFrameClass;
             CRuntimeClass* pViewClass;
 
             (*pClassInfo)(clsidClassID,clsidClassCategory,
                           csDescription,pObjectClass,pFrameClass,
                           pViewClass);
 
             if (GetClassInfo(clsidClassID))
                 AfxMessageBox(
                 "CClassBroker::LoadDLLModules - Duplicate class ID");
             else
                 AddClassInfo(clsidClassID,clsidClassCategory,
                              csDescription,pObjectClass,pFrameClass,
                              pViewClass,csDLL);
         }
 
         AfxFreeLibrary(hModule);
     }
 }

Figure 10   View Broker Code

OpenView Method of CViewBroker


 void CViewBroker::OpenView(LPCTSTR pszObjectKey)
 {
     CViewInfo* pViewInfo;
     if (m_mapViewInfo.Lookup(pszObjectKey,(void*&)pViewInfo))
     {
         CView* pView = (CView*)pViewInfo->m_listViews.GetHead();
         CMDIChildWnd* pFrameWindow =
             (CMDIChildWnd*)pView->GetParent();
         if (pFrameWindow->IsIconic())
             pFrameWindow->MDIRestore();
         pFrameWindow->MDIActivate();
         return;
     }
 
     pViewInfo = new CViewInfo();
     m_mapViewInfo.SetAt(pszObjectKey,pViewInfo);
 
     OpenDuplicateView(pszObjectKey);
 }
OpenDuplicateView Method of CViewBroker

 void CViewBroker::OpenDuplicateView(LPCTSTR pszObjectKey)
 {
     CObjectInfo* pObjectInfo = m_pObjectBroker->
         GetObjectInfoByKey(pszObjectKey);
     CClassInfo* pClassInfo = 
         CClassBroker::GetClassInfo(pObjectInfo->GetClassID());
     CMultiDocTemplateEx* pDocTemplate = 
         (CMultiDocTemplateEx*)m_pObjectDoc->GetDocTemplate();
 
     CFrameWnd* pFrameWnd = pDocTemplate->CreateNewFrame(
         m_pObjectDoc,pClassInfo->GetFrameClass(),
         pClassInfo->GetViewClass());
 
     CView* pView =
         (CView*)pFrameWnd->
             GetDescendantWindow(AFX_IDW_PANE_FIRST,TRUE);
     ASSERT(pView->IsKindOf(pClassInfo->GetViewClass()));
 
     CViewInfo* pViewInfo = NULL;
     m_mapViewInfo.Lookup(pszObjectKey,(void*&)pViewInfo);
     ASSERT(pViewInfo);
     pViewInfo->m_listViews.AddTail(pView);
 
     ObjectInfoMessage structObjectInfoMessage;
     structObjectInfoMessage.pObjectBroker = m_pObjectBroker;
     structObjectInfoMessage.pszObjectKey = pszObjectKey;
     VERIFY(pView->SendMessage(WM_OBJECTINFO,0,
         (LPARAM)&structObjectInfoMessage));
 
     SetWindowTitles();
     pDocTemplate->InitialUpdateFrame(pFrameWnd,m_pObjectDoc);
 }
CreateNewFrame Method of CMultiDocTemplateEx

 CFrameWnd* CMultiDocTemplateEx::CreateNewFrame(CDocument* pDoc, 
     CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass)
 {
     if (pFrameClass)
         m_pFrameClass = pFrameClass;
     else
         m_pFrameClass = m_pDefaultFrameClass;
 
     if (pViewClass)
         m_pViewClass = pViewClass;
     else
         m_pViewClass = m_pDefaultViewClass;
 
     return CMultiDocTemplate::CreateNewFrame(pDoc,NULL);
}

Figure 13   Object Broker Code

Add Method of CObjectBroker


 BOOL CObjectBroker::Add(REFCLSID rclsid, CString& rcsKey)
 {
     CString csName, csStream;
 
     GenerateUniqueKey(rcsKey);
     GenerateUniqueName(csName);
     GenerateUniqueStream(csStream);
     
     CObjectInfo* pObjectInfo = new CObjectInfo;
 
     pObjectInfo->SetKey(rcsKey);
     pObjectInfo->SetName(csName);
     pObjectInfo->SetStream(csStream);
     pObjectInfo->SetObjectBroker(this);
 
     if(!pObjectInfo->SetClassID(rclsid))
     {
         delete pObjectInfo;
         return FALSE;
     }
 
     m_mapObjectsByKey.SetAt(rcsKey,pObjectInfo);
     m_mapObjectsByName.SetAt(csName,pObjectInfo);
     m_mapObjectsByStream.SetAt(csStream,pObjectInfo);
 
     NotifyObservers(OBJECT_BROKER_ADDED_OBJECT,&rcsKey);
 
     return TRUE;
 }
SetClassID Method of CObjectInfo

 BOOL CObjectInfo::SetClassID(REFCLSID rclsid)
 {
     ASSERT(m_pDynamicObject == NULL);
 
     m_clsid = rclsid;
 
     CClassInfo* pClassInfo = GetClassInfo();
     if (pClassInfo == NULL)
         return FALSE;
 
     pClassInfo->LoadLibrary();
 
     CRuntimeClass* pObjectClass = pClassInfo->GetObjectClass();
     ASSERT(pObjectClass);
 
     if (!pObjectClass->IsDerivedFrom(RUNTIME_CLASS(CDynamicObject)))
         return FALSE;
     
     m_pDynamicObject = 
         (CDynamicObject*)pObjectClass->CreateObject();
 
     ASSERT(m_pDynamicObject);
     ASSERT(m_pDynamicObject->IsKindOf(RUNTIME_CLASS(CDynamicObject)));
 
     m_pDynamicObject->SetObjectInfo(this);
     m_pDynamicObject->OnCreatedNewObject();
 
     StoreObject(TRUE);
 
     return TRUE;
 }
StoreObject Method of CObjectInfo

 void CObjectInfo::StoreObject(BOOL bFreeMemory)
 {
     LPSTORAGE pObjectStorage = GetObjectBroker()->GetObjectStorage();
 
     COleStreamFile StreamFile;
     if (StreamFile.OpenStream(pObjectStorage,GetStream())== FALSE)
         VERIFY(StreamFile.CreateStream(pObjectStorage,GetStream()));
 
     CArchive Archive(&StreamFile,
         CArchive::store|CArchive::bNoFlushOnDelete);
     Archive.m_bForceFlat = FALSE;
 
     Archive << m_pDynamicObject;
 
     Archive.Close();
     StreamFile.Close();
     pObjectStorage->Commit(STGC_DEFAULT);
     pObjectStorage->Release();
 
     if (bFreeMemory)
     {
         delete m_pDynamicObject;
         m_pDynamicObject = NULL;
     }
 }
GetObject Method of CObjectInfo

 CDynamicObject* CObjectInfo::GetObject()
 {
     if (m_pDynamicObject)
     {
         m_pDynamicObject->AddRef();
         return m_pDynamicObject;
     }
 
     LPSTORAGE pObjectStorage = GetObjectBroker()->GetObjectStorage();
 
     COleStreamFile StreamFile;
     VERIFY(StreamFile.OpenStream(pObjectStorage,GetStream()));
 
     CArchive Archive(&StreamFile,
         CArchive::load|CArchive::bNoFlushOnDelete);
     Archive.m_bForceFlat = FALSE;
 
     Archive >> m_pDynamicObject;
 
     Archive.Close();
     StreamFile.Close();
     pObjectStorage->Release();
     
     ASSERT(m_pDynamicObject);
     ASSERT(m_pDynamicObject->IsKindOf(RUNTIME_CLASS(CDynamicObject)));
 
     m_pDynamicObject->SetObjectInfo(this);
     m_pDynamicObject->AddRef();
     m_pDynamicObject->OnSerializedObjectFromArchive();
 
     return m_pDynamicObject;
 }
GetObjectStorage Method of CObjectBroker

 LPSTORAGE CObjectBroker::GetObjectStorage()
 {
     BSTR bstrObjectStorage = m_csObjectStorageName.AllocSysString();
 
     LPSTORAGE pObjectStorage = NULL;
 
     HRESULT hr = m_pRootStorage->OpenStorage(bstrObjectStorage,  
                           NULL,STGM_SHARE_EXCLUSIVE|STGM_READWRITE|STGM_TRANSACTED,
                           NULL,0,&pObjectStorage);
     ASSERT(SUCCEEDED(hr));
 
     SysFreeString(bstrObjectStorage);
 
     return pObjectStorage;
 }
SetRootStorage Method of CObjectBroker

 void CObjectBroker::SetRootStorage(LPSTORAGE pRootStorage)
 {
     if (m_pRootStorage == pRootStorage)
         return;
 
     BSTR bstrObjectStorage = m_csObjectStorageName.AllocSysString();
 
     if (m_pRootStorage == NULL)
     {
         LPSTORAGE pObjectStorage = NULL;
 
         m_pRootStorage = pRootStorage;
 
         if (FAILED(m_pRootStorage->OpenStorage(bstrObjectStorage,
             NULL,STGM_SHARE_EXCLUSIVE|STGM_READWRITE,
             NULL,0,&pObjectStorage)))
 
             m_pRootStorage->CreateStorage(bstrObjectStorage,
                 STGM_SHARE_EXCLUSIVE|STGM_READWRITE,
                 0,0,&pObjectStorage);
                     
         pObjectStorage->Release();
     }
     else
     {
 
         m_pRootStorage->MoveElementTo(bstrObjectStorage,
             pRootStorage,bstrObjectStorage,STGMOVE_COPY);
         m_pRootStorage = pRootStorage;
     }
 
     SysFreeString(bstrObjectStorage);
 }
OnNewDocument Method and Serialize Method of CObjectDoc

 BOOL CObjectDoc::OnNewDocument()
 {
     if (!COleDocument::OnNewDocument())
         return FALSE;
 
     m_pObjectBroker->SetRootStorage(m_lpRootStg);
 
     return TRUE;
 }
 
 void CObjectDoc::Serialize(CArchive& ar)
 {
     COleDocument::Serialize(ar);
 
     m_pObjectBroker->SetRootStorage(m_lpRootStg);
     m_pObjectBroker->Serialize(ar);
 }

Figure 18   OVRotor.dll

ClassInfo Function Exported from OVRotor.dll


 extern "C" void WINAPI ClassInfo(CLSID& clsidClassID, 
     CLSID& clsidClassCategory, CString& csDescription,
     CRuntimeClass*& pObjectClass, CRuntimeClass*& pFrameClass,
     CRuntimeClass*& pViewClass)
 {
     // ID: {4423E281-664E-11d0-8945-2AFFD5000000}
     CLSID clsidID = { 0x4423e281, 0x664e, 0x11d0, 
         { 0x89, 0x45, 0x2a, 0xff, 0xd5, 0x0, 0x0, 0x0 } };
 
     // CATEGORY: {A8D09C01-90C4-11d0-A264-0040052E01FC}
     CLSID clsidCategory = { 0xa8d09c01, 0x90c4, 0x11d0, 
         { 0xa2, 0x64, 0x0, 0x40, 0x5, 0x2e, 0x1, 0xfc } };
 
     clsidClassID = clsidID;
     clsidClassCategory = clsidCategory;
 
     csDescription = "Rotor";
 
     pObjectClass = RUNTIME_CLASS(CRotor);
     pFrameClass = RUNTIME_CLASS(CFlexibleChildFrame);
     pViewClass = RUNTIME_CLASS(CRotorView);
 }
Constructor for CRotorView

 CRotorView::CRotorView()
     : CNSFlexFormView(CRotorView::IDD)
 {
     //{{AFX_DATA_INIT(CRotorView)
         // NOTE: the ClassWizard will add member initialization here
     //}}AFX_DATA_INIT
 
     m_pRotor = NULL;
 
     AddFlexConstraint(IDC_ROTORS,        
         NSFlexHorizontallyFixed,NSFlexExpandDown);
     AddFlexConstraint(IDC_LOCATOR,         
         NSFlexExpandRight,NSFlexExpandDown); 
 }
OnObjectInfo Method of CRotorView

 LRESULT CRotorView::OnObjectInfo(WPARAM wParam, LPARAM lParam)
 {
     ObjectInfoMessage* pObjectInfoMessage = 
         (ObjectInfoMessage*)(lParam);
 
     CObjectBroker* pObjectBroker = pObjectInfoMessage->pObjectBroker;
 
     CObjectInfo* pObjectInfo = 
         pObjectBroker->GetObjectInfoByKey(pObjectInfoMessage->
         pszObjectKey);
     ASSERT(pObjectInfo);
 
     m_pRotor = (CRotor*)pObjectInfo->GetObject();
     ASSERT(m_pRotor);
     ASSERT(m_pRotor->IsKindOf(RUNTIME_CLASS(CRotor)));
 
     m_pRotor->AttachToSubject(this);
 
     return 1;
 }
Serialize Method of CRotor

 void CRotor::Serialize(CArchive& archive)
 {
     CDynamicObject::Serialize(archive);
 
     if (archive.IsStoring())
     {
         archive << m_nSelectedRotor;
     }
     else
     {
         archive >> m_nSelectedRotor;
     }
 
 }
OnUpdate Method of CRotorView

 void CRotorView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
 {
     if (!m_bNeedUpdate)
         return;
 
     CListBox* pRotorsList = (CListBox*)GetDlgItem(IDC_ROTORS);
     for (int ii = 0; ii < m_pRotor->GetNumRotors(); ii++)
     {
         if ((int)pRotorsList->GetItemData(ii) == 
             m_pRotor->GetSelectedRotor())
         {
             pRotorsList->SetCurSel(ii);
             break;
         }
     }
 
     Invalidate();
 
     m_bNeedUpdate = FALSE;
 }
OnSelchangeRotors Method of CRotorView

 void CRotorView::OnSelchangeRotors() 
 {
     CListBox* pRotorsList = (CListBox*)GetDlgItem(IDC_ROTORS);
     int nRotor = pRotorsList->GetItemData(pRotorsList->GetCurSel());
 
     m_pRotor->SelectRotor(nRotor);
 
     GetDocument()->SetModifiedFlag();
     GetDocument()->UpdateAllViews(NULL);
 }
SelectRotor Method of CRotor

 void CRotor::SelectRotor(int nIndex)
 {
     ASSERT(nIndex >= 0 && nIndex <= NUM_ROTORS);
     m_nSelectedRotor = nIndex;
 
     NotifyObservers(ROTOR_CHANGED,NULL);
 }
SubjectChanged Method of CRotorView

 void CRotorView::SubjectChanged(CSubject* pSubject, 
     LPARAM lHint, void* pHint)
 {
     ASSERT(pSubject == m_pRotor);
 
     switch(lHint)
     {
 
         case ROTOR_CHANGED:
             m_bNeedUpdate = TRUE;
             break;
 
         default:
             ASSERT(FALSE);
 
     }
 
 }
Destructor for CRotorView

 CRotorView::~CRotorView()
 {
     m_pRotor->DetachFromSubject(this);
     m_pRotor->Release();
 }

Figure 21   OVRotorDriver.dll

Serialize Method of CRotorDriver


 void CRotorDriver::Serialize(CArchive& archive)
 {
 
     if (archive.IsStoring())
     {
         archive << m_bRotorIsGoing;
         archive << m_nRotationIncrement;
         archive << m_nUpdateInterval;
         archive << m_nRotorPosition;
         archive << m_bRotateClockwise;
         archive << m_csRotorKey;
     }
     else
     {
         archive >> m_bRotorIsGoing;
         archive >> m_nRotationIncrement;
         archive >> m_nUpdateInterval;
         archive >> m_nRotorPosition;
         archive >> m_bRotateClockwise;
         archive >> m_csRotorKey;
     }
 
 }
OnSerializedObjectFromArchive Method of CRotorDriver

 void CRotorDriver::OnSerializedObjectFromArchive()
 {
     AttachToObjectBroker();
     AttachToRotor();
 
     m_pTriggerThreadInfo->m_nUpdateInterval = m_nUpdateInterval;
 
     m_pTriggerThread =
     AfxBeginThread(TriggerThreadProc,m_pTriggerThreadInfo);
     ASSERT(m_pTriggerThread);
 
     if (m_bRotorIsGoing)
         SetEvent(m_pTriggerThreadInfo->m_hEventStartInterval);
 }
AttachToObjectBroker Method of CRotorDriver

 void CRotorDriver::AttachToObjectBroker()
 {
     GetObjectBroker()->AttachToSubject(this);
 }
AttachToRotor Method of CRotorDriver

 void CRotorDriver::AttachToRotor()
 {
     DetachFromRotor();
 
     CObjectInfo* pObjectInfo = 
         GetObjectBroker()->GetObjectInfoByKey(m_csRotorKey);
 
     if (pObjectInfo)
     {
         m_pRotor = (CGenericRotor*)pObjectInfo->GetObject();
         m_pRotor->AttachToSubject(this);
     }
     else
     {
         m_csRotorKey.Empty();
     }
 }
DetachFromRotor Method of CRotorDriver

 void CRotorDriver::DetachFromRotor()
 {
     if (m_pRotor)
     {
         m_pRotor->DetachFromSubject(this);
         m_pRotor->Release();
         m_pRotor = NULL;
     }
 }
SubjectChanged Method of CRotorDriver

 void CRotorDriver::SubjectChanged(CSubject* pSubject, 
                                   LPARAM lHint, void* pHint)
 {
     if (pSubject == m_pRotor)
     {
         switch(lHint)
         {
             case ROTOR_CHANGED:
                 NotifyObservers(ROTOR_DRIVER_ROTOR_CHANGED,NULL);
                 return;
         }
 
         ASSERT(FALSE);
     }
 
     if (pSubject == GetObjectBroker())
     {
         switch(lHint)
         {
             case OBJECT_BROKER_ABOUT_TO_REMOVE_OBJECT:
                 if (m_csRotorKey == *((CString*)pHint))
                     SetRotorKey("");
                 return;
             
             case OBJECT_BROKER_ADDED_OBJECT:
             case OBJECT_BROKER_REMOVED_OBJECT:
             case OBJECT_BROKER_RENAMED_OBJECT:
                 return;
         }
 
         ASSERT(FALSE);
     }
 
     ASSERT(FALSE);
 }
SetRotorKey Method of CRotorDriver

 void CRotorDriver::SetRotorKey(LPCSTR pszRotorKey)
 {
     m_csRotorKey = pszRotorKey;
 
     AttachToRotor();
 
     NotifyObservers(ROTOR_DRIVER_SELECTED_ROTOR_CHANGED,NULL);
 }
Destructor for CRotorDriver

 CRotorDriver::~CRotorDriver()
 {
     DWORD dwExitCode;
     if (GetExitCodeThread(m_pTriggerThread->m_hThread, &dwExitCode) &&
         dwExitCode == STILL_ACTIVE)
     {
         SetEvent(m_pTriggerThreadInfo->m_hEventKillTriggerThread);
         SetEvent(m_pTriggerThreadInfo->m_hEventStartInterval);
         WaitForSingleObject(
             m_pTriggerThreadInfo->m_hEventTriggerThreadKilled,
             INFINITE);
     }
 
     delete m_pTriggerThreadInfo->m_pThreadMessageWnd;
     delete m_pTriggerThreadInfo;
 
     DetachFromRotor();
     DetachFromObjectBroker();
 }
DetachFromObjectBroker Method of CRotorDriver

 void CRotorDriver::DetachFromObjectBroker()
 {
     GetObjectBroker()->DetachFromSubject(this);
}

Figure 23   OVUserRotor.dll

OnInitDialog Method of CPropertiesDlg


 BOOL CPropertiesDlg::OnInitDialog() 
 {
     // ROTOR CLASS CATEGORY: {A8D09C01-90C4-11d0-A264-0040052E01FC}
     CLSID clsidRotorClassCategory = { 0xa8d09c01, 0x90c4, 0x11d0, 
         { 0xa2, 0x64, 0x0, 0x40, 0x5, 0x2e, 0x1, 0xfc } };
 
     CComboBox* pRotors = (CComboBox*)GetDlgItem(IDC_ROTORS);
     
     CObjectBroker* pObjectBroker = m_pRotorDriver->GetObjectBroker();
 
     for (POSITION pos = pObjectBroker->GetStartPosition(); pos; )
     {
         CString csKey;
         CObjectInfo* pObjectInfo;
 
         pObjectBroker->GetNextAssoc(pos,csKey,pObjectInfo);
 
         CClassInfo* pClassInfo = CClassBroker::GetClassInfo(
             pObjectInfo->GetClassID());
 
         if (pClassInfo->GetClassCategory() == clsidRotorClassCategory)
         {
             int nIndex = pRotors->AddString(pObjectInfo->GetName());
             pRotors->SetItemDataPtr(nIndex,pObjectInfo);
             if (m_pRotorDriver->GetRotorKey() == pObjectInfo->GetKey())
             {
                 pRotors->SetCurSel(nIndex);
                 m_csRotorKey = pObjectInfo->GetKey();
             }
         }
 
     }
 
     pRotors->InsertString(0,"<None>");
     if (pRotors->GetCurSel() == CB_ERR)
         pRotors->SetCurSel(0);
 
     CDialog::OnInitDialog();
     
     return TRUE;  // return TRUE unless you set the focus to a control
                   // EXCEPTION: OCX Property Pages should return FALSE
 }
ClassInfo Function Exported from OVUserRotor.dll

 extern "C" void WINAPI ClassInfo(CLSID& clsidClassID, 
                      CLSID& clsidClassCategory, CString& csDescription,
                      CRuntimeClass*& pObjectClass, CRuntimeClass*& pFrameClass,
     CRuntimeClass*& pViewClass)
 {
     // ID: {08EA0501-90EF-11d0-A264-0040052E01FC}
     CLSID clsidID = { 0x8ea0501, 0x90ef, 0x11d0, 
         { 0xa2, 0x64, 0x0, 0x40, 0x5, 0x2e, 0x1, 0xfc } };
 
     // CATEGORY: {A8D09C01-90C4-11d0-A264-0040052E01FC}
     CLSID clsidCategory = { 0xa8d09c01, 0x90c4, 0x11d0, 
         { 0xa2, 0x64, 0x0, 0x40, 0x5, 0x2e, 0x1, 0xfc } };
 
     clsidClassID = clsidID;
     clsidClassCategory = clsidCategory;
 
     csDescription = "User Rotor";
 
     pObjectClass = RUNTIME_CLASS(CUserRotor);
     pFrameClass = RUNTIME_CLASS(CFlexibleChildFrame);
     pViewClass = RUNTIME_CLASS(CUserRotorView);
 }