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);
}