Визуальное программирование и MFC

         

MainFrm.cpp


// MainFrm.cpp : implementation of the CMainFrame class //   #include "stdafx.h" #include "single.h"   #include "MainFrm.h"   #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif   ///////////////////////////////////////////////////////////////////////////// // CMainFrame   IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)   BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code ! ON_WM_CREATE() //}}AFX_MSG_MAP END_MESSAGE_MAP()   static UINT indicators[] = { ID_SEPARATOR, // status line indicator ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL, };   ///////////////////////////////////////////////////////////////////////////// // CMainFrame construction/destruction   CMainFrame::CMainFrame() { // TODO: add member initialization code here }   CMainFrame::~CMainFrame() { }   int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; if (!m_wndToolBar.Create(this) !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { TRACE0("Failed to create toolbar\n"); return -1; // fail to create }   if (!m_wndStatusBar.Create(this) !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status bar\n"); return -1; // fail to create }   // TODO: Remove this if you don't want tool tips or a resizeable toolbar m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);   // TODO: Delete these three lines if you don't want the toolbar to // be dockable m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar);   return 0; }   BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs   return CFrameWnd::PreCreateWindow(cs); }   ///////////////////////////////////////////////////////////////////////////// // CMainFrame diagnostics   #ifdef _DEBUG void CMainFrame::AssertValid() const { CFrameWnd::AssertValid(); }   void CMainFrame::Dump(CDumpContext& dc) const { CFrameWnd::Dump(dc); }   #endif //_DEBUG   ///////////////////////////////////////////////////////////////////////////// // CMainFrame message handlers


// MainFrm.cpp : implementation of the CMainFrame class //

#include "stdafx.h" #include "multi.h"

#include "MainFrm.h"

#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif

///////////////////////////////////////////////////////////////////////////// // CMainFrame

IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd) //{{AFX_MSG_MAP(CMainFrame) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code ! ON_WM_CREATE() //}}AFX_MSG_MAP END_MESSAGE_MAP()

static UINT indicators[] = { ID_SEPARATOR, // status line indicator ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL, };

///////////////////////////////////////////////////////////////////////////// // CMainFrame construction/destruction

CMainFrame::CMainFrame() { // TODO: add member initialization code here }

CMainFrame::~CMainFrame() { }

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; if (!m_wndToolBar.Create(this) !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { TRACE0("Failed to create toolbar\n"); return -1; // fail to create }

if (!m_wndStatusBar.Create(this) !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status bar\n"); return -1; // fail to create }

// TODO: Remove this if you don't want tool tips or a resizeable toolbar m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);

// TODO: Delete these three lines if you don't want the toolbar to // be dockable m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar);

return 0; }

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs

return CMDIFrameWnd::PreCreateWindow(cs); }



///////////////////////////////////////////////////////////////////////////// // CMainFrame diagnostics

#ifdef _DEBUG void CMainFrame::AssertValid() const { CMDIFrameWnd::AssertValid(); }

void CMainFrame::Dump(CDumpContext& dc) const { CMDIFrameWnd::Dump(dc); }

#endif //_DEBUG

///////////////////////////////////////////////////////////////////////////// // CMainFrame message handlers



MainFrm.h


// MainFrm.h : interface of the CMainFrame class // ///////////////////////////////////////////////////////////////////////////// #if !defined(AFX_MAINFRM_H__A7203678_E01B_11D1_9525_0080488929D2__INCLUDED_) #define AFX_MAINFRM_H__A7203678_E01B_11D1_9525_0080488929D2__INCLUDED_   #if _MSC_VER >= 1000 #pragma once #endif // _MSC_VER >= 1000   class CMainFrame : public CFrameWnd { protected: // create from serialization only CMainFrame(); DECLARE_DYNCREATE(CMainFrame)   // Attributes public:   // Operations public:   // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CMainFrame) virtual BOOL PreCreateWindow(CREATESTRUCT& cs); //}}AFX_VIRTUAL   // Implementation public: virtual ~CMainFrame(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif   protected: // control bar embedded members CStatusBar m_wndStatusBar; CToolBar m_wndToolBar;   // Generated message map functions protected: //{{AFX_MSG(CMainFrame) afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG DECLARE_MESSAGE_MAP() };   /////////////////////////////////////////////////////////////////////////////   //{{AFX_INSERT_LOCATION}} // Microsoft Developer Studio will insert additional declarations immediately before the previous line.   #endif // !defined(AFX_MAINFRM_H__A7203678_E01B_11D1_9525_0080488929D2__INCLUDED_)


// MainFrm.h : interface of the CMainFrame class // /////////////////////////////////////////////////////////////////////////////

#if !defined(AFX_MAINFRM_H__A720368C_E01B_11D1_9525_0080488929D2__INCLUDED_) #define AFX_MAINFRM_H__A720368C_E01B_11D1_9525_0080488929D2__INCLUDED_

#if _MSC_VER >= 1000 #pragma once #endif // _MSC_VER >= 1000

class CMainFrame : public CMDIFrameWnd { DECLARE_DYNAMIC(CMainFrame) public: CMainFrame();

// Attributes public:

// Operations public:

// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CMainFrame) virtual BOOL PreCreateWindow(CREATESTRUCT& cs); //}}AFX_VIRTUAL

// Implementation public: virtual ~CMainFrame(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif

protected: // control bar embedded members CStatusBar m_wndStatusBar; CToolBar m_wndToolBar;

// Generated message map functions protected: //{{AFX_MSG(CMainFrame) afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG DECLARE_MESSAGE_MAP() };

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}} // Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MAINFRM_H__A720368C_E01B_11D1_9525_0080488929D2__INCLUDED_)



Макрокоманда ON_UPDATE_COMMAND_UI


Макрокоманда ON_UPDATE_COMMAND_UI для использования в таблицах сообщений приложения и имеет следующий формат:

ON_UPDATE_COMMAND_UI(id, Имя_метода)

Параметр id определяет идентификатор строки меню, кнопки панели управления или индикатора панели состояния, для которых надо обработать команду обновления. Параметр Имя_метода задает метод, выполняющий обновление.

Если один и тот же метод вызывается для обработки различных макрокоманд обновления, можно использовать другую макрокоманду - ON_UPDATE_COMMAND_UI_RANGE. Она вызывает метод Имя_метода для обработки всех команд обновления, идентификаторы которых находятся в промежутке значений от id1 до id2:

ON_UPDATE_COMMAND_UI_RANGE(id1, id2, MethodName)

Наилучшим способом создания обработчиков команд обновления и включения соответствующих макрокоманд в таблицу сообщений приложения является использование средства ClassWizard.

Однако назначить один обработчик команд обновления нескольким объектам пользовательского интерфейса ClassWizard не позволяет. При необходимости можно самостоятельно включить в таблицу сообщений макрокоманду ON_UPDATE_COMMAND_UI_RANGE.

Еще одно ограничение ClassWizard заключается в том, что он не дает возможности создать обработчики для команд обновления от индикаторов панели состояния. Такие обработчики придется добавлять к класса приложения вручную.

Рассмотрим описание метода-обработчика команд обновления. Вызывающийся макрокомандами ON_UPDATE_COMMAND_UI_RANGE и ON_UPDATE_COMMAND_UI метод-обработчик команд обновления имеет следующий формат:

afx_msg void Имя_метода(CCmdUI *pCmdUI);

Имя метода-обработчика обычно формируется из префикса OnUpdate и названия соответствующего объекта интерфейса пользователя - строки меню, кнопки панели управления или индикатора панели состояния.

В качестве параметра этому методу передается указатель на объект класса CCmdUI. Этот объект представляет элемент пользовательского интерфейса (строку меню, кнопку панели управления, индикатор панели состояния), для которого надо обработать команду обновления. Вызывая методы класса CCmdUI, можно легко изменять состояние соответствующего объекта интерфейса пользователя.



Массивы, списки, словари


В состав MFC включен целый набор классов, предназначенных для хранения информации в массивах, списках и словарях. Все эти классы наследованы от базового класса CObject.

Несмотря на то, что в языке C определено понятие массива, классы MFC обеспечивают более широкие возможности. Например, можно динамически изменять размер массива, определенного с помощью соответствующего класса.

Для представления массивов предназначены следующие классы:

CByteArray - байты.

CDWordArray - двойные слова.

CObArray - указатели на объекты класса CObject.

CPtrArray - указатели типа void.

CStringArray - объекты класса CString.

CUIntArray - элементы класса unsigned integer или UINT.

CWordArray - слова.

Для решения многих задач применяются такие структуры хранения данных, как списки. MFC включает ряд классов, наследованных от базового класса CObject, которые представляют программисту готовое для создания собственных списков. В этих классах определены все методы, необходимые при работе со списками, - добавление нового элемента, вставка нового элемента, определение следующего или предыдущего элемента в списке, удаление элемента и т.д.

Перечислим классы списков, которые позволяют построить списки из элементов любых типов любых классов:

CObList - указатели на объекты класса CObject.

CPtrList - указатели типа void.

CStringList - объекты класса CString.

В библиотеке MFC определена еще одна группа классов, позволяющая создавать словари. Словарь представляет собой таблицу из двух колонок, устанавливающих соответствие двух величин. Первая величина представляет ключевое значение и записывается в первую колонку, а вторая - связанное с ней значение, хранящееся во второй колонке. Словарь позволяет добавлять в него пары связанных величин и осуществлять выборку значений по ключевому слову.

Для работы со словарями используются классы:

CMapPtrToPtr - ключевое слово - указатель типа void, связанное с ним значение - указатель типа void.

CMapPtrToWord - ключевое слово - указатель типа void, связанное с ним значение - слово.

CMapStringToOb - ключевое слово - объекты класса CString, связанное с ним значение - указатель на объекты класса CObject.

CMapStringToPtr - ключевое слово - объекты класса CString, связанное с ним значение - указатель типа void.

CMapStringToString - ключевое слово - объекты класса CString, связанное с ним значение - на объекты класса CObject.

CMapWordToOb - ключевое слово - слово, связанное с ним значение - указатель на объекты класса CObject.

CMapWordToPtr- ключевое слово - слово, связанное с ним значение - указатель типа void.



Механизм записи и восстановления объектов


Рассмотрим предлагаемую MFC модель обмена данными между документом и файлом, в котором этот процесс идет через промежуточный объект-архив класса CArchive библиотеки MFC. Этот объект передается методу Serialize класса документа как параметр. Поэтому программист посылает данные не в файл, а в архив и читает из архива.

Метод Serialize класса документа вызывается объектами-архивами, когда приложение при необходимости читает или записывает состояние этого документа, вызывая методы CArchive::ReadObject и CArchive::WriteObject для объекта-архива. При этом методу Serialize передается ссылка на объект-архив, который и вызывает методы чтения или записи.

Из архива данные передаются в файл или читаются из файла. Объект-архив всегда связан с файлом (объектом класса CFile). Диалог при открытии и сохранении документа позволяет задать имя физического файла, хранящего данные документа. Затем создается объект класса CFile, который связывается с объектом архивом. Заметим, что создаваемый автоматически объект-архив существует на время выполнения только одной операции - чтения документа или записи документа, после чего разрушается.

Рассмотрим процесс записи и восстановления документа для однооконного или многооконного приложения, создаваемого средством AppWizard. При выборе команд Open, Save, SaveAs каркас приложения:

Выводит соответствующее диалоговое окно для получения имени файла от пользователя.

Открывает файл, указанный пользователем, как объект класса CFile. Создает объект-архив класса CArchive, связывая его с объектом файлом. Объект-архив получает статус "store" или "load", в зависимости от того, будет ли данные документа записываться или восстанавливаться из архива.

Вызывает метод Serialize, определенный в классе документа приложения, производном от класса CDocument, используя методы WriteObject или ReadObject для объекта-архива.

Определенный разработчиком приложения метод Serialize класса документа запишет значения переменных документа в архив.
Через буфер архива данные передаются в файл, связанный с объектом-архивом.

Завершив сохранение документа, каркас приложения разрушит созданный объект-архив, а затем и объект-файл.

В этом процессе на разработчика приложения возлагается единственная обязанность - определить метод Serialize класса документа. Он отвечает только за то, какие переменные документа и как будут записываться в архив.

Если программист сам организует процесс сериализации данных документа, то он сам должен проделать все перечисленные выше действия. Допустим, приложение выводит окно, которое имеет меню, содержащее пункты "Store" и "Load". Класс окна тогда имеет примерно следующее описание:

// фрагмент файла определения класса class CMyWnd : public CFrameWnd { protected: CMyDoc *readDoc; // указатель на объект класса документа CMyDoc writeDoc; // объект класса документа

protected: afx_msg void OnStore(); afx_msg void OnLoad(); DECLARE_MESSAGE_MAP() // другие описания класса ........................ };

При выборе пользователем из меню пункта "Store" вызывается метод-обработчик OnStore, а при выборе пункта "Load" - метод-обработчик OnLoad. Приведем примеры организации процесса сохранения и восстановления документа.

// фрагмент файла реализации класса BEGIN_MESSAGE_MAP(CMyWnd, CFrameWnd) ON_COMMAND(ID_LOAD, OnLoad) ON_COMMAND(ID_STORE, OnStore) END_MESSAGE_MAP() void CMyWnd::OnLoad() { // создание стандартной панели выбора файла Open CFileDialog DlgOpen(TRUE,NULL,NULL, OFN_HIDEREADONLY,(LPCSTR)"Все файлы (*.*)|*.*");

// отображение стандартной панели выбора файла Open if(DlgOpen.DoModal()==IDOK) { // открытие файла для чтения CStdioFile File(DlgOpen.GetPathName(),CFile::modeRead);

// связывание файла с объектом класса CArchive CArchive ar(&File,CArchive::load);

// запись документа readDoc=(CMyDoc *)ar.ReadObject(RUNTIME_CLASS(CMyDoc));

// закрыть объект ar и файл, связанный с ним ar.Close(); File.Close();

// данные из переменных класса документа помещаются // в переменные класса окна ............................................. } } void CMyWnd::OnStore() { // создание стандартной панели выбора файла SaveAs CFileDialog DlgSaveAs(FALSE,NULL,NULL, OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, (LPCSTR)"Все файлы (*.*)|*.*");

// отображение стандартной панели выбора файла SaveAs if(DlgSaveAs.DoModal()==IDOK) { // данные из переменных класса окна помещаются // в переменные класса документа ....................................................

// открытие файла для записи CFile File(DlgSaveAs.GetPathName(), CFile::modeCreate|CFile::modeWrite);

// связывание файла с объектом класса CArchive CArchive ar(&File,CArchive::store);

// запись документа ar.WriteObject(&writeDoc);

// закрыть объект ar и файл, связанный с ним ar.Close(); File.Close(); } } // другие методы класса .......................


Меню без явного создания объекта класса CMenu


Для управления меню, как и для других элементов пользовательского интерфейса, в состав библиотеки MFC включен специальный класс - класс CMenu. Класс CMenu является достаточно “незаметным” классом библиотеки MFC. Приложение может активно работать с меню, но все же в его исходных текстах можно не найти ни одного объекта этого класса.

В приложениях, созданных с помощью MFC AppWizard, меню создается автоматически вместе с панелью управления и панелью состояния. Для этого достаточно указать при создании шаблона документа общий идентификатор этих ресурсов, например:

CMultiDocTemplate* pDocTemplate; // указатель на шаблон pDocTemplate = new CMultiDocTemplate // создание шаблона (IDR_MULTITYPE, RUNTIME_CLASS(CMultiDoc), RUNTIME_CLASS(CChildFrame), RUNTIME_CLASS(CMultiView)); AddDocTemplate(pDocTemplate); // добавить шаблон в список

В случае многооконного приложения дополнительно указываются ресурсы, используемые, когда все окна просмотра документов закрыты. Все эти ресурсы имеют один и тот же идентификатор, например:

CMainFrame* pMainFrame = new CMainFrame; // создание главного окна if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) // загрузка ресурсов return FALSE; m_pMainWnd = pMainFrame; // соединение главного окна и объекта приложения

В приложениях MFC AppWizard, имеющих однооконный или многооконный интерфейс, меню создается и изменяется самой библиотекой MFC. Несмотря на это, программист может сам управлять меню. Самым простым способом является обработка команд обновления от меню (см. ниже описание класса CCmdUI).

Если приложение создано без использования средств AppWizard, то даже в этом случае процедура создания меню остается очень простой и также может не задействовать объекты класса CMenu напрямую.

В таких приложениях обычно формируется главное окно на основе класса CFrameWnd. Для этого сначала создается объект класса CFrameWnd, а затем вызывается либо метод Create, либо метод LoadFrame, который в свою очередь уже строит само окно вместе с меню.

Метод Create




Это метод создает и инициализирует окно, связанное с объектом CFrameWnd:

BOOL Create( LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle = WS_OVERLAPPEDWINDOW, const RECT& rect = rectDefault, CWnd* pParentWnd = NULL, LPCTSTR lpszMenuName = NULL, DWORD dwExStyle = 0, CCreateContext* pContext = NULL );

Обязательно нужно указать только два первых параметра метода Create - имя класса окна (если использовать NULL, то по умолчанию будет использоваться класс, определенный для окон CFrameWnd )и имя окна (его заголовок).

Через параметр lpszMenuName можно задать имя ресурса меню, которое будет создано для данного окна. Например, вот как можно создать окно c меню, шаблон которого определен в ресурсах и имеет идентификатор IDR_MENU (создание окна производится в конструкторе класса окон CMyFrame, наследуемого от CFrameWnd):

CMyFrame::CMyFrame() { Create(NULL, "Окно с меню ",WS_OVERLAPPEDWINDOW, rectDefault,NULL,MAKEINTRESOURCE(IDR_MENU)); }



Метод LoadFrame



Виртуальный метод LoadFrame позволяет динамически создавать меню, пользуясь информацией из файла ресурсов:

virtual BOOL LoadFrame( UINT nIDResource, DWORD dwDefaultStyle= WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, CWnd* pParentWnd = NULL, CCreateContext* pContext=NULL);

Параметры этого метода почти идентичны параметрам метода Create, описанного ранее. Исключение составляет параметр nIDResource. Он представляет собой идентификатор, общий для нескольких ресурсов, употребляемых при создании окна (меню, строковый ресурс заголовка окна, пиктограмма и таблица клавиш акселерации):

CMyFrame::CMyFrame() { LoadFrame(IDR_MENU); }


Меню (класс CMenu)


Практически каждое приложение имеет собственное меню. Оно, как правило, отображается в верхней части главного окна приложения. Для управления меню в состав MFC включен специальный класс CMenu, наследованный непосредственно от базового класса CObject.

Для управления меню и панелями используется также класс CCmdUI. Этот класс не наследуется от базового класса CObject.

Объекты класса CCmdUI создаются, когда пользователь выбирает строку меню или нажимает кнопки панели управления. Методы класса CCmdUI позволяют управлять строками меню и кнопками панели управления. Например, существует метод, который делает строку меню неактивной.



Методы автоматизации


Методы автоматизации – это функции-члены класса элемента управления, которые образуют программный интерфейс элемента управления и доступны внешнему миру посредством OLE-автоматизации. Другими словами, они вызываются контроллерами автоматизации и сообщают элементу управления, что он должен сделать.

В общем случае методы автоматизации обладают характерными чертами, такими, как:

Внешнее имя метода – это его текстовое, читабельное имя, доступное внешнему миру. Контроллер автоматизации использует данное имя для вызова метода.

Внутреннее имя – это его имя в исходных файлах. У элемента управления может существовать метод автоматизации, который доступен внешнему миру с одним именем, а внутри класса реализован с другим. Внутренние и внешние имена методов могут быть как одинаковыми, так и разными. Выбор этого полностью зависит от разработчика.

Идентификатор диспетчеризации (DISPID) – это числовой идентификатор, который позволяет различать отдельные свойства, методы и события. У каждого метода есть соответствующий ему DISPID. Эти идентификаторы используются главным образом в odl-файле элемента управления, а интерфейс Idispatch определяет соответствие между текстовым именем метода и его DISPID.

Методы реализуются в виде обычных методов C++. Ограничением является лишь то, что типы параметров и возвращаемых значений должны поддерживать механизм транспортировки (marshaling). Следует помнить, что смысл автоматизации заключается в том, что она позволяет одной программе управлять работой другой программы. Чтобы обеспечить поддержку автоматизации, следует использовать типы семейства VARIANT (VT_xxx).

Методы автоматизации делятся на две основные категории: базовые и пользовательские. Фирма Microsoft определила и реализовала ряд базовых методов автоматизации. Refresh (заставляет элемент перерисовать себя), DoClick (имитирует щелчок мышью на элементе управления) и AboutBox() (отображает на экране модальное диалоговое окно “About” элемента управления). Все эти методы имеют отрицательные DISPID.

Все остальные методы элемента управления определяет разработчик. Такие методы называются пользовательскими. Пользовательские методы, наряду с базовыми, составляют интерфейс автоматизации элемента управления. Эти методы позволяют программам-клиентам управлять внешним видом и поведением элемента управления.



Методы для рисования линий и фигур


Пикселы

Для установки цвета пикселя с логическими координатами (x,y) используются метод SetPixel. Получить значение цвета пикселя можно методом GetPixel.

Цвет задается функцией RGB. Как уже отмечалось выше, если в физической палитре нет данного цвета, задаваемого фактическим параметром, Windows устанавливает наиболее близкий цвет. Он-то и возвращается методом SetPixel. Следует также отметить, что, хотя координаты являются логическими, устанавливается цвет только одного пикселя, даже если единица измерения для текущей системы координат иная.

Линии

Прежде, чем описывать методы для рисования линий, рассмотрим два важных для них атрибута контекста устройства.

Первый называют текущим пером. Его значением является перо как объект GDI. Любая линия (в том числе и ограничивающая фигуру) рисуется пером. Если метод не содержит явного параметра, задающего перо, то для рисования берется текущее перо, которое можно установить методом SelectObject. Или, если в качестве параметра передать одну из констант BLACK_PEN, NULL_PEN, WHITE_PEN, то методом SelectStockObject

Второй важный атрибут - текущая позиция пера. Чтобы изменить координаты текущей позиции пера, используются метод MoveTo. Чтобы нарисовать прямую линию от текущей позиции пера до нужной точки с логическими координатами (x,y), используется метод LineTo. После выполнения метода LineTo заданная в нем точка становится текущей позицией пера.

Если имеется массив точек и требуется соединить линией каждую следующую точку с предыдущей, можно использовать метод PolyLine, в котором первый параметр - указатель на массив элементов типа POINT, а второй равен количеству точек. При выполнении этого метода текущая позиция пера не изменяется.

Следующий метод аналогичен PolyLine за исключением того, что он устанавливает текущую позицию пера равной последней точке массива – PolyLineTo.

Если же требуется соединить между собой все точки, содержащиеся в массиве, можно вызвать метод PolyPolyline.

Фигуры

В Visual C++ имеются методы для рисования: прямоугольника (Rectangle); эллипса (Ellipse); скругленного прямоугольника (RoundRect); сегмента эллипса (Chord); сектора эллипса (Pie); замкнутого многоугольника; составного замкнутого многоугольника.

Для рисования фигур важен атрибут контекста устройства, называемый текущей кистью. Он задает кисть как объект GDI, с помощью которого производится закрашивание внутренней области фигуры. Текущая кисть устанавливается методом SelectObject или SelectStockObject, если в качестве параметра передать одну из констант: BLACK_BRUSH, DKGRAY_BRUSH, GRAY_BRUSH, HOLLOW_BRUSH, LTGRAY_BRUSH, NULL_BRUSH, WHITE_BRUSH.



Методы инициализации


Методы создания Windows-окон обсуждаться не будут, так как при работе со многими классами окна (к которым относятся и элементы управления) создаются каркасом прилржения. Рассмотрим только методы для прикрепления и открепления Windows-окон.

BOOL Attach(HWND hWndNew);

Закрепляет Windows-окно, заданное определителем hWndNew за оконным объектом. При успешном выполнении возвращает ненулевое значение, в противном случае - 0.

HWND Detach();

Открепляет Windows-окно, закрепленное за данным оконным объектом, и возвращает определитель открепленного окна.



Методы класса CArchive


Перед тем, как создать объект-архив класса CArchive, необходимо создать объект класса CFile. Связывая с объектом CFile файл на диске, нужно иметь в виду, что если нужно записать данные, то файл нужно открывать на запись, а если считать - то для чтения.

Конструктор класса CArchive имеет следующий вид:

CArchive(CFile* pFile, UINT nMode, int nBufSize=512, void* lpBuf=NULL);

Параметр pFile должен содержать указатель на объект класса CFile, из которого будут считываться или записываться данные. Перед вызовом конструктора файл, связанный с объектом pFile, должен быть уже открыт.

Параметр nMode определяет, будут ли данные записываться в файл или считываться из него: CArchive::load (данные будут считываться из файла на диске), CArchive::store (данные будут записываться в файл на диске).

Для операций чтения и записи в файл класс CArchive выполняет буферизацию. Размер этого буфера определяется необязательным параметром nBufSize. По умолчанию используется буфер размером 512 байт.

Конструктор класса CArchive сам получает у операционной системы блок оперативной памяти для буфера. Однако с помощью необязательного параметра lpBuf программист может предоставить ему собственный буфер. В этом случае параметр nBufSize должен указывать размер этого буфера.

Созданный объект класса CArchive можно будет использовать только для записи или только для чтения. Если необходимо сначала считать данные, а потом записать, следует создать для этого два отдельных объекта класса CArhive.

Нельзя применять методы класса CFile для доступа к файлу во время его использования совместно с объектами класса CArchive. Запись, чтение или перемещение указателя файла может вызвать нарушения во внутренней структуре файла архива.

Как уже отмечалось, метод Serialize класса документа вызывается объектами-архивами, когда приложение при необходимости читает или записывает состояние этого документа, вызывая методы CArchive::ReadObject и CArchive::WriteObject для объекта-архива. При этом методу Serialize передается ссылка на объект-архив, который и вызывает методы чтения или записи.


После использования методов CArchive::ReadObject и CArchive::WriteObjec t для восстановления или записи объектов, необходимо закрыть используемый для этого объект класса CArchive. Для этого нужно вызвать метод CArchive::Close.

После вызова этого метода нужно закрыть файл, cвязанный с объектом CArchive, вызвав метод CFile::Close, и удалить сам объект класса CFile.



Запись в архивный файл



Когда приложение желает сохранить состояние объекта в файле, оно вызывает для него метод Serialize. В качестве параметра этому методу передается указатель на объект класса CArhive, связанный с файлом, открытым на запись.

Для того, чтобы определить, предназначен ли объект-архив для записи или для чтения, можно вызвать методы CArchive::IsLoading или CArchive::IsStoring.

Метод IsStoring возвращает ненулевое значение, если данный объект предназначен для записи в файл, и нуль в противном случае. Метод IsLoading является прямой противоположностью метода IsStoring. Можно использовать любой метод для определения режима работы с объектом-архивом.

В случае записи в архивный файл реализация метода должна сохранить в файле все элементы данных, которые потом потребуется восстановить. Для этого необходимо воспользоваться оператором << или методами WriteString и Write, определенными в классе CArchive.

Оператор << можно использовать, для записи в архивный файл переменных простых типов, например, long, int, char и объектов других классов, наследованных от класса CObject.

Для записи в архивный файл массивов удобнее использовать метод Write класса CArchive. Он позволяет записать в файл определенное количество байт из указанного буфера памяти.

Если требуется сохранить строку символов, закрытую нулем, то гораздо удобнее вместо метода Write использовать метод WriteString. Метод WriteString записывает в архивный файл строку, ограниченную нулевым символом.



Чтение из архивного файла



Опишем теперь, как восстановить записанное ранее состояние объекта из архивного файла.


Когда приложение желает восстановить состояние объекта данного класса, оно вызывает для него метод Serialize. В качестве параметра этому методу передается указатель на объект класса CArchive, связанного с файлом, открытым для чтения.

Реализация метода Serialize должна восстановить из файла все элементы данных, которые в него были записаны. Для этого можно воспользоваться оператором >> или методами ReadString и Read, определенными в классе CArchive.

Данные из архивного файла должны считываться в том же порядке, в каком они были в него записаны.

Оператор >> можно использовать, для чтения из архивного файла переменных простых типов, например, long, int, char и объектов других классов, наследованных от класса CObject.

Для чтения из архивного файла массивов, записанных в него методом Write, надо использовать метод Read класса CArchive. Он позволяет прочитать из файла определенное количество байт и записать его в указанный буфер.

Если требуется прочитать из архивного файла строку, записанную в него методом WriteString, нужно воспользоваться методом ReadString. В состав класса CArchive входят два метода ReadString, которые предназначены для записи из файла строки в объект класса CString или в обычную строку.

Если надо записать прочитанную из архивного файла строку в массив символов, нужно воспользоваться другим прототипом метода ReadString.


Методы класса CCmdUI


Класс CCmdUI играет важную роль в работе таких объектов пользовательского интерфейса как меню, панели управления и панели состояния. Методы этого класса позволяют заблокировать отдельные элементы меню, панелей управления и состояния, отметить их и т.д.

Также этими методами можно пользоваться для блокирования элементов диалоговых панелей управления, для выбора положения переключателей в этих панелях.

Рассмотрим немногочисленные методы этого класса.

Метод EnableЭтот виртуальный метод позволяет установить или снять блокировку с объекта пользовательского интерфейса, представленного объектом класса CСmdUI.

Заблокированные элементы не могут быть использованы до момента снятия блокировки. В случае блокировки индикатора панели состояния его текст не будет отображаться.

Метод SetCheck. Виртуальный метод SetCheck можно применять для изменения состояния строки меню и кнопок панели управления.

При использовании этого метода для строки меню нулевое значение параметра приводит к выделению строки меню символом O

, а единичное значение - к снятию выделения.

В случае применения метода SetCheck для кнопки панели управления параметр метода задает новое состояние кнопки: нулевое значение - кнопка переходит в нажатое состояние, единичное значение - кнопка переходит в отжатое состояние, если параметр равен двум - кнопка переходит в промежуточное состояние.

Этот же метод используется и для управления внешним видом индикаторов панелей состояния. Если параметр метода равен нулю, то рамка индикатора изменяется таким образом, что он будет располагаться выше общего уровня панели состояния. Если параметр равен единице, то индикатор переходит в нормальное состояние.

Метод SetRadio

Виртуальный метод SetRadio можно применять для изменения состояния строки меню и кнопок панели управления.

Если метод используется для управления меню, то значение TRUE параметра приводит к выделению строки меню символом ·

. Если параметр равен FALSE, то выделение снимается.

В случае использования метода SetRadio для кнопкой панели управления параметр метода задает новое состояние кнопки.
Значение FALSE переводит кнопку в нажатое состояние, значение TRUE - в отжатое состояние.

Этот же метод используется и для управления внешним видом индикаторов панелей состояния. Если параметр метода равен FALSE, то рамка индикатора изменяется таким образом, что он будет располагаться выше общего уровня панели состояния. Если параметр равен TRUE, то индикатор переходит в нормальное состояние.



Метод SetText



Виртуальный метод SetText может быть вызван для изменения текста, отображаемого в панелях состояния, в строке меню, в названиях кнопок и некоторых элементах диалоговых панелей управления. В качестве параметра следует указать строку, которую нужно вывести.

Следует отметить, что при использовании метода SetText для изменения текста в панелях состояния необходимо отдельно позаботиться об изменение размера индикатора. Метод SetText не меняет размера индикатора, потому следует рассчитать ширину текста и изменить размер индикатора с помощью соответствующего метода.



Элементы данных класса CcmdUI



В состав класса CCmdUI входит и несколько элементов данных. Они позволяют определить идентификатор строки меню, кнопки панели управления или индикатора панели состояния, для которого вызван метод-обработчик:

m_nID - идентификатор объекта, для которого вызван метод-обработчик.

m_nIndex - индекс объекта, для которого вызван метод-обработчик.

m_pMenu - указатель на меню. Если команда обновления передана не от меню, элемент m_pMenu содержит значение NULL.

m_pOther - указатель на панель состояния или панель управления. Если команда обновления передана от меню, элемент m_pOther содержит значение NULL.


Методы класса диалоговой панели управления


Непосредственно в состав класса CDialogBar входят всего два метода - конструктор класса и метод Create.

Конструктор класса только создает соответствующий объект, но не саму панель управления. Чтобы создать панель управления, следует вызвать метод Create и указать ему шаблон диалоговой панели, используемый для создания диалоговой панели.

Метод Create имеет два различных формата:

BOOL Create( CWnd* pParentWnd, LPCTSTR lpszTemplateName, UINT nStyle, UINT nID ); BOOL Create( CWnd* pParentWnd, UINT nIDTemplate, UINT nStyle, UINT nID );

Первый параметр задает окно, для которого создается диалоговая панель управления. Обычно в качестве такого окна выступает главное окно приложения.

Второй параметр указывает шаблон панели управления. В зависимости от того, как ресурс панели определен в ресурсах приложения, указывается либо идентификатор, либо имя ресурса.

Третий параметр определяет начальное положение панели управления в окне приложения. Можно использовать следующие флаги: CBRS_TOP, CBRS_BOTTOM, CBRS_LEFT, CBRS_RIGHT.

Последний параметр определяет идентификатор диалоговой панели управления.



Методы управления размером и положением окна


void MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint=TRUE);

void MoveWindow(LPCRECT lpRect, BOOL bRepaint=TRUE);

Эти методы изменяют положение и размеры окна. Положение левого верхнего угла окна задантся координатами x,y, а размеры шириной nWidth и высотой nHeight. Параметр bRepaint определяет, будет ли инициироваться перерисовка. Если он равен TRUE, окну будет послано сообщение WM_PAINT, в противном случае сообщение не посылается, и перерисовка не производится. Эти действия применяются как к клиентской, так и неклиентской области окна, а также к частям родительского окна, открывшимся при перемещении.

Новое положение и размеры окна можно задать и с помощью структуры типа RECT или объекта класса CRect, передав в качестве параметра ссылку на структуру или объект класса.

Для окна, у которого нет родителя, координаты указываются относительно левого верхнего угла экрана, а для имеющего такового - относительно верхнего левого угла родительского окна.

BOOL SetWindowPos(const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFgags);

Изменяет положение, размеры и место окна в Z-упорядочении (порядке изображения окон в слоях изображения). Параметры x, y, cx, cy задают новое положение левой стороны, новое положение верхней стороны, новую длину и новую высоту соответственно.

Параметр pWndInsertAfter определяет окно, за которым нужно поместить исходное окно в Z-упорядочении. Этот параметр может быть либо указателем на объект класса CWnd, либо одним из следующих значений:

wndBottom поместить окно в конец Z-упорядочения, т.е. позади всех окон на экране;

wndTop поместить окно в начало Z-упорядочения, т.е. впереди всех окон на экране;

wndTopMost поместить окно на ближайшее место, делающее окно неперекрытым. Окно перемещается на неперекрытое место, даже если оно неактивно;

wndNoTopMost поместить окно на ближайшее место позади всех неперекрытых окон. Окно, перекрытое в момент вызова этого метода, не перемещается.

Параметр nFlags задает режим изменения размера и положения окна и может быть следующей комбинацией флагов:


SWP_DRAWFRAME изображать вокруг окна рамку (определенную при его создании);

SWP_HIDEWINDOW скрыть окно;

SWP_NOACTIVE не делать окно активным. Если этот флаг не установлен, окно делается активным и помещается впереди, либо на место в Z-упорядочении, определяемое параметром pWndInsertAfter;

SWP_NOMOVE сохранить текущее положение окна, проигнорировав параметры x и y;

SWP_NOREDRAW не перерисовывать измененное окно. Если этот флаг установлен, то после выполнения функции окно с новыми установками на экране не появится, а старое изображение окна не будет стерто с родительского окна;

SWP_NOSIZE сохранить текущий размер окна, проигнорировав параметры cx и cy;

SWP_NOZORDER сохранить текущее Z-упорядочение, проигнорировав параметр pWndInsertAfter;

SWP_SHOWWINDOW показать окно (сделать окно видимым и перерисовать).

Если окно не является дочерним, координаты указываются относительно левого верхнего угла экрана, иначе координаты указываются относительно верхнего левого угла клиентской области родительского окна. Приложение не может активизировать неактивное окно, не поместив его в начало Z-упорядочения. Приложение не может изменить место активного окна в Z-упорядочении произвольным образом. Перекрытые окна могут быть родительскими по отношению к неперекрытым, но не наоборот.



void GetWindowRect(LPRECT lpRect) const;



Копирует параметры прямоугольника, ограничивающего окно, в структуру типа RECT или объект класса CRect, заданные параметром lpRect. Этот прямоугольник включает все - и клиентскую и системную часть окна. Параметры даются относительно левого верхнего угла экрана. При вызове метода значением фактического параметра может быть либо ссылка на (не константную) структуру типа RECT либо объект класса CRect.



void GetClientRect(LPRECT lpRect) const);



Копирует параметры прямоугольника, ограничивающего клиентскую часть окна, в структуру типа RECT или объект класса CRect, определенные параметром lpRect. Параметры даются относительно левого верхнего угла клиентской области окна, поэтому левая и верхняя составляющие будут равны нулю, а правая и нижняя - ширине и длине клиентской области.При вызове метода параметр lpRect может быть либо указателем на структуру типа RECT, либо переменной класса CRect.


Методы управления состоянием окна


Сначала рассмотрим четыре типа состояний:

Активное/неактивное. Активным является окно, с которым пользователь работает в текущий момент. Оно находится поверх всех окон и выделено цветом заголовка. Окно активизируется после щелчка мыши в любой его точке.

В фокусе/не в фокусе клавиатуры. Окну в фокусе клавиатуры направляются сообщения, вызванные нажатием клавиш на клавиатуре. Как правило, активное окно как раз и находится в фокусе клавиатуры. Исключения составляют дочерние окна, которые сами по себе не могут быть активными или неактивными, но при этом могут быть в фокусе клавиатуры. В этом случае активным должно быть родительское окно.

Мышь захвачена/не захвачена. Все сообщения от мыши, захваченной окном, направляются этому окну независимо от позиции курсора на экране.Только одно окно может захватить мышь в текущий момент.

Включенное/выключенное (доступное/недоступное). Если окно во выключенном состоянии, пользователь не может сделать его активным или передать ему фокус. Операционная система игнорирует любые сообщения окну от клавиатуры и мыши.

Перечислим некоторые методы управления состоянием окна.

BOOL IsWindowEnabled() const;

Если окно в выключенном состоянии, возвращает нулевое значение, в противном случае - 0.

BOOL EnableWindow(BOOL bEnable=TRUE);

Если значение параметра TRUE, окно переводится в включенное состояние, FALSE - в выключенное. Метод возвращает ненулевое значение, если в момент вызова окно находилось в выключенном состоянии. Если окно было во включенном состоянии или произошла ошибка, возвращается 0.

CWnd* SetActiveWindow();

Переводит окно в активное состояние. Возвращает указатель на оконный объект, обеспечивающий доступ к окну, активному в момент вызова этого метода (указатель может быть временным и не должен запоминаться для дальнейшего использования).

static CWnd* PASCAL GetActiveWindow();

Возвращает указатель на оконный объект, обеспечивающий доступ к окну, активному в момент вызова этого метода. Если в момент вызова активных окон нет, возвращается NULL.
Этот указатель может быть временным и не должен запоминаться для дальнейшего использования.



CWnd* SetCapture();



Переводит окно в состояние захвата мыши. Возвращает указатель на оконный объект, обеспечивающий доступ к окну, которым мышь была захвачена в момент вызова этого метода. Если в момент вызова мышь не захвачена, возвращает NULL. Этот указатель может быть временным и не должен запоминаться для дальнейшего использования. Чтобы освободить мышь, используется API-функция ReleaseCapture (параметров не имеет). При успешном ее выполнении возвращается TRUE, иначе - FALSE.



static CWnd* PASCAL GetCapture();



Возвращает указатель, задающий окно, захватившее мышь. Если такого окна нет, возвращает NULL.



BOOL ModifyStyle(DWORD dwRemove, DWORD dwAdd, UINT nFlags=0);



Изменяет стиль окна. Параметр dwRemove задает набор элементов стиля, которые должны быть изъяты из стиля окна. Параметр dwAdd - набор элементов стиля, которые должны быть добавлены к стилю окна. Возвращает ненулевое значение, если стиль был успешно изменен, в противном случае - 0.

Если параметр nFlags не равен 0, то после изменения стиля вызывается API-функция SetWindowPos, которая перерисовывает окно, используя набор флагов, полученный комбинацией значения:

SWP_NOSIZE - сохранять текущий размер;

SWP_NOMOVE - сохранять текущую позицию;

SWP_NOZORDER - сохранять текущий Z-порядок;

SWP_NOACTIVE - не делать окно активным.


Методы управления текстом окна


void SetWindowText(LPCTSTR lpszString);

Устанавливает текст заголовка окна. Если окно - элемент управления, устанавливает текст в этом элементе. При вызове функции параметр должен быть либо указателем на строку символов, оканчивающуюся нулевым символом, либо переменной типа CString.

int GetWindowText(LPSTR lpszStringBuf, int nMaxCount) const;

void GetWindowText(CString& rString) const;

Копирует текст из заголовка окна. Если окно - элемент управления, копирует текст из этого элемента. При вызове первого варианта функции параметр lpszStringBuf должен быть указателем на буфер, в который будет скопирован текст, а параметр nMaxCount - выражением, задающим размер буфера (максимальное число символов, которое разрешается скопировать в буфер). Функция возвращает число скопированных символов, не включающее нуль-символ. При вызове второго варианта параметр rString должен быть переменной типа CString.

int GetWindowTextLength() const;

Возвращает длину текста заголовка окна, а если окно является элементом управления - длину текста этого элемента. Длина не учитывает нуль-символ.

CFont* GetFont() const;

Получает текущий шрифт данного окна.

void SetFont(CFont* pFont, BOOL bRedraw=TRUE);

Устанавливает текущий шрифт окна. Параметр pFont должен задавать новое значение для текущего шрифта. Если параметр bRedraw равен TRUE, окно после установки нового шрифта перерисовывается.



Методы взаимодействия Windows-окон


BOOL UpdateData(BOOL bSaveAndValidate=TRUE);

Выполняет обмен данными между объектом класса, производного от CWnd, и частным случаем Windows-окна - диалоговым окном. Если параметр равен FALSE, данные будут пересылаться от объекта и обновлять содержимое элементов управления диалогового окна. В противном случае данные будут считываться из элементов управления диалогового окна и обновлять переменные оконного объекта. При выполнении этого метода происходит вызов виртуального метода DoDataExchange класса CWnd. Эту функция нужно переопределить, чтобы она выполнила весь необходимый обмен. Как правило, в качестве объектов, обменивающихся данными с диалоговым окном, выступают объекты пользовательских классов, производных от CDialog или CFormView. Для них имеется возможность создания переменных, связанных с элементом управления. В этом случае работу по созданию переопределенной функции DoDataExchange выполняет ClassWizard.



MFC-cерверы автоматизации


Итак, автоматизация представляет собой механизм обмена информацией между взаимодействующими приложениями. Приложения, являющиеся серверами автоматизации, обладают способностью отображать свои объекты с помощью свойств и методов. Приложения, которые являются клиентами автоматизации или контроллерами автоматизации, могут получить доступ к этим отображаемым свойствам и методам через OLE-интерфейс IDispatch.

Рассмотрим, как создать собственый сервер автоматизации с помощью библиотеки MFC. Для быстрого и легкого создания необходимого класса и методов используются мастера AppWizard и ClassWizard.



MFC-контроллер автоматизации


Для создания COM-клиентов можно использовать различные среды разработки. Рассмотрим, как создавать контроллеры автоматизации в cреде Microsoft Developer Studio с помощью MFC. Создадим, например, при помощи средства MFC AppWizard (exe) проект приложения Cnt, основанного на диалоговой панели.

Затем в проект приложения-контроллера Cnt следует добавить специальный контроллерный класс, обеспечивающий работу приложения с COM-объектом. Для этого необходимо вызвать ClassWizard, нажать на кнопку “Add class…” и выбрать пункт “From a type library…”. В появившемся окне следует из каталога проекта сервера автоматизации выбрать файл SvrExe.tlb (или файл библиотеки типов другого COM-сервера). Сгенерированные мастером ClassWizard файлы SvrExe.cpp и SvrExe.h автоматически добавятся в проект контроллера автоматизации Cnt.

Для того, чтобы создать COM-объект сервера, необходимо сначала объявить объект obj контроллерного класса ISimpleMFC (того класса, который создал мастер ClassWizard по библиотеке типов сервера SvrExe.tlb), наследованного от класса ColeDispatchDriver.

Затем для объекта obj следует вызвать его метод CreateDispatch (метод его базового класса), который загружает серверную программу и получает указатель на интерфейс IDispatch COM-объекта (сохраняемый в переменной m_lpDispatch класса COleDispatchDriver):

… AfxOleInit(); // инициализация библиотек OLE BOOL result=obj.CreateDispatch("SvrExe.SimpleMFC"); if(result!=TRUE) AfxMessageBox("COM-object didn't create"); …

Для вызова методов COM-объекта, входящих в состав интерфейса ISimpleMFC, следует воспользоваться вызовами в следующем формате:

ТипВозвращаемогоЗначения ИмяПеременной=obj.ИмяМетода(Параметры);

например,

obj.Initialize(2L);



Модальная диалоговая панель


Итак, первым этапом работы с диалоговыми панелями является создание в файле ресурсов приложения шаблона новой диалоговой панели с каким-либо идентификатором (например, IDD_DIALOG1) и изменение его для конкретных целей (например, добавление поля редактирования с идентификатором IDC_EDIT1 и кнопки с идентификатором IDC_CLEAR).

Затем при помощи средства ClassWizard можно создать класс диалога (например, CDlg), производный от базового класса CDialog и организующий работу диалоговой панели (ClassWizard сохраняет определение и реализацию этого класса в файлах с расширениями *.cpp и *.h).

При помощи инструментов ClassWizard можно добавить в класс диалоговой панели заготовки методов-обработчиков оконных сообщений, сообщений от органов управления (например, нажатие на кнопки “OK”, “Cancel” и “Clear”) и др. Для обеспечения процесса обмена данными средствами ClassWizard можно связать элементы управления с переменными (например поле редактирования с идентификатором IDC_EDIT1 с переменной str класса CString).

Класс диалоговой панели и его реализация

В итоге выполнения перечисленных действий по созданию класса диалога средство ClassWizard сгенегрирует следующие файлы:

Файл dlg.h // dlg.h : header file //

#include "resource.h"

///////////////////////////////////////////////////////////////////////////// // CDlg dialog

class CDlg : public CDialog { // Construction public: CDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data //{{AFX_DATA(CDlg) enum { IDD = IDD_DIALOG1}; CString m_Str; //}}AFX_DATA

// Implementation protected: // DDX/DDV support virtual void DoDataExchange(CDataExchange* pDX);

// Generated message map functions //{{AFX_MSG(CDlg) afx_msg void OnClickedClear(); virtual void OnCancel(); virtual void OnOK(); virtual BOOL OnInitDialog(); //}}AFX_MSG DECLARE_MESSAGE_MAP() };

Файл dlg.cpp // dlg.cpp : implementation file // #include <afxwin.h> #include "resource.h" #include "dlg.h"

#ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif


///////////////////////////////////////////////////////////////////////////// // CDlg dialog

CDlg::CDlg(CWnd* pParent /*=NULL*/) : CDialog(CDlg::IDD, pParent) { //{{AFX_DATA_INIT(CDlg) m_Str=””; //}}AFX_DATA_INIT }

void CDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CDlg) DDX_Text(pDX, IDC_EDIT1, m_Str); //}}AFX_DATA_MAP }

BEGIN_MESSAGE_MAP(CDlg, CDialog) //{{AFX_MSG_MAP(CDlg) ON_BN_CLICKED(IDC_CLEAR, OnClickedClear) //}}AFX_MSG_MAP END_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////////// // CDlg message handlers

BOOL CMainDlg::OnInitDialog() { CDialog::OnInitDialog();

// TODO: Add extra initialization here

return TRUE; // return TRUE unless you set the focus to a control }

void CDlg::OnClickedClear() { // TODO: Add your control notification handler code here m_Str=""; UpdateData(FALSE); }

void CDlg::OnCancel() { // TODO: Add your control notification handler code here MessageBeep(0); CDialog::OnCancel(); }

void CDlg::OnOK() { // TODO: Add your control notification handler code here MessageBeep(0); CDialog::OnOK(); }



Объявление класса



Класс СDlg наследуется от базового класса CDialog, определенного в библиотеке классов MFC. Конструктор класса имеет один необязательный параметр pParent, используемый для передачи индекса главного окна приложения.

Непосредственно после объявления конструктора класса следует объявление элементов данных класса, которые добавлены средствами MFC ClassWizard. (Не рекомендуется вручную изменять код приложения, расположенный между комментариями AFX_DATA, между которыми заключены эти объявления.)

Далее ClassWisard добавляет объявления переопределенных виртуальных методов базового класса. Сначала объявлен только метод DoDataExchange, переопределенный для данной диалоговой панели.

Диалоговая панель CMainDlg будет обрабатывать ряд сообщений. Объявления обработчиков сообщений, созданных при помощи ClassWizard, располагаются между комментариями AFX_MSG.





Конструктор класса



Следует обратить внимание на определение конструктора класса CDlg. После названия конструктора стоит двоеточие и название конструктора класса CDialog. При этом в качестве параметров ему передается идентификатор диалоговой панели и указатель на родителькое окно:

Основное назначение конструктора CDlg - вызвать конструктор класса CDialog. Именно конструктор класса CDialog выполняет создание диалоговой панели.

В теле конструктора расположен блок AFX_DATA_INIT. В него ClassWizard будет добавлять код инициализации элементов данных класса CDlg. В данное время там инициализируется переменная m_Str, входящая в класс CDlg, - в нее записывается строка текста. С этой переменной связано поле редактирования с идентификатором IDC_EDIT1. При помощи такой связи реализуется процесс обмена данными между элементами управления и переменными класса.



Метод OnInitDialog



При отображении диалоговой панели при помощи функций DoModal (Create или CreateIndirect для немодальных диалогов), функция диалоговой панели передается сообщение WM_INITDIALOG. Непосредственного доступа к функции диалога нет. Ее реализация содержится в классе CDialog.

В ответ на сообщение WM_INITDIALOG вызывается метод OnInitDialog, объявленный как виртуальный метод класса CDialog. Этот метод вызывается непосредственно перед выводом панели на экран.

Таблица сообщений класса CDlg не содержит макрокоманд для обработки сообщения WM_INITDIALOG. Метод OnInitDialog вызывается непосредственно MFC. Чтобы реализовать собственную обработку сообщения WM_INITDIALOG, нужно просто переопределить метод OnInitDialog.

Метод OnInitDialog возвращает значение TRUE. Это означает, что фокус ввода будет установлен на первый орган управления диалоговой панели. Первый орган диалоговой панели можно выбрать в редакторе диалоговой панели.

Если во время инициализации диалоговой панели метод OnInitDialog устанавливает фокус ввода другому органу управления, метод должен вернуть значение FALSE.



Обмен данными диалога





Виртуальный метод DoDataExchange, который также переопределяется в классе диалоговой панели, первоначально определен в классе CWnd. Он служит для реализации механизмов автоматического обмена данными - Dialog Data Exchange (DDX) - и автоматической проверки данных - Dialog Data Validation (DDV).



Механизм автоматического обмена данными позволяет привязать к органам управления диалоговой панели переменные или элементы данных класса диалоговой панели. Ряд специальных функций, определенных в библиотеке MFC, вызываются методом DoDataExchange и выполняют обмен данными между органами управления диалоговой панели и соответствующими элементами данных класса диалоговой панели. Такой обмен работает в обоих направлениях. Информация из органов управления диалоговой панели может записываться в элементы данных класса, или, в обратном направлении, информация из элементов данных класса может отображаться в диалоговой панели.

Название всех функций, обеспечивающих обмен данными, начинаются с префикса DDX_. Практически каждый тип органов управления диалоговой панели имеет собственную функцию для выполнения процедуры обмена данными.

Все функции DDX_ имеют три параметра. Первый параметр содержит указатель на объект класса CDataExchange. Этот объект определяет параметры обмена, в том числе направление, в котором надо выполнить обмен данными. Второй параметр определяет идентификатор органа управления, с которым выполняется обмен данными. Третий параметр содержит ссылку на элемент данных класса диалоговой панели, связанный с данным органом управления.

Метод DoDataExchange позволяет выполнять проверку данных, которые пользователь вводит в диалоговой панели. Для этого предназначен ряд функций DDV_. Эти функции позволяют гарантировать, что данные, введенные пользователем в диалоговой панели, соответствуют определенным условиям.



Если функция DDV_ используется для проверки ввода в данном органе управления диалоговой панели, то ее необходимо вызвать сразу же после вызова функции DDX_ для этого же органа управления.





Если функция DDV_ обнаруживает ошибку пользователя при вводе информации в органе управления, она отображает сообщение и передает фокус ввода соответствующему органу управления.

В отличие от функций DDX_ функции DDV_ в зависимости от их предназначения имеют различное количество параметров. Первый параметр, как и в случае DDX_, содержит указатель на объект класса CDataExchange. Остальные параметры имеют различное назначение в зависимости от функций.



Приложение не должно напрямую вызывать метод DoDataExhange. Он вызывается через метод UpdateData, определенный в классе CWnd. Необязательный параметр этой функции определяет, как будет происходить обмен данными.



Если метод UpdateData вызывается с параметром FALSE (см. метод CDlg::OnClickedClear), то выполняется инициализация диалоговой панели. Информация из данных класса отображается в органах управления диалоговой панели.



В случае, если метод UpdateData вызван с параметром TRUE, данные перемещаются в обратном направлении. Из органов управления диалоговой панели они копируются в соответствующие элементы данных класса диалоговой панели.



Метод UpdateData возвращает ненулевое значение, если обмен данными прошел успешно, и нуль в противном случае. Ошибка при обмене данными может произойти, если данные копируются из диалоговой панели в элементы класса диалоговой панели и пользователь ввел неправильные данные, отвергнутые процедурой автоматической проверки данных.

При создании модальной диалоговой панели перед тем, как панель появится на экране, вызывается виртуальный метод OnInitDialog класса CDialog. По умолчанию OnInitDialog вызывает метод UpdateData и выполняет инициализацию органов управления. Если метод OnInitDialog переопределяется в классе диалоговой панели, в первую очередь необходимо вызвать метод OnInitDialog класса CDialog.

Метод UpdateData также вызывается некоторыми другими методами класса CDialog. Так, метод UpdateData вызывается, когда пользователь закрывает модальную диалоговую панель, нажимает кнопку "Ok".


Заметим, что кнопка "Ok" должна иметь идентификатор IDOK. Если пользователь нажмет на кнопку "Cancel", имеющую идентификатор IDCANCEL, то диалоговая панель также закрывается, но метод UpdateData не вызывается и обмен данными не происходит.

Методу DoDataExchange, который служит для реализации механизмов автоматического обмена данными, передается указатель pDX на объект класса CDataExchange. Этот объект создается, когда инициируется процесс обмена данными вызовом функции UpdateData. Элементы данных класса CDataExchange определяют процедуру обмена данными, в том числе определяют, в каком направлении будет происходить этот обмен. Следует обратить внимание на то, что указатель pDX передается функциям DDX_ и DDV_.

Если к диалоговой панели добавить новые органы управления и связать их средствами ClassWizard с элементами данных класса CDlg, то в блоке AFX_DATA_MAP будут размещены вызовы и других функций DDX и DDV, необходимые для выполнения обмена данными.



Таблица сообщений диалоговой панели



Класс диалоговой панели должен обрабатывать сообщения от своих органов управления, поэтому он должен иметь таблицу сообщений. В заголовке таблицы сообщений указывается имя класса CDlg и имя базового класса CDialog. Таблица сообщений класса CDlg содержит только одну строку, в которой обрабатывается сообщение с кодом извещения ON_BN_CLICKED от кнопки "Clear". Когда пользователь нажимает кнопку, вырабатывается данное сообщение и вызывается его обработчик - метод OnClickedClear, определенный в классе CDlg.

Строки таблицы сообщений расположены в блоке AFX_MSG_MAP, поэтому для управления ими используется ClassWizard.

Две другие кнопки панели - "Ok" и "Cancel" - не представлены в таблице сообщений, но в приложении определены методы OnOK и OnCancel, которые вызываются при нажатии на них. Оказывается, для диалоговых панелей определены две стандартные кнопки - "Ok" и "Cancel", которым присвоены специальные идентификаторы IDOK и IDCANCEL.





Базовый класс CDialog, так же как и класс CMyDialog, содержит таблицу сообщений. Среди прочих сообщений в этой таблице определены командные сообщения с идентификаторами IDOK и IDCANCEL. Для обработки этих командных сообщений определены виртуальные методы OnOK и OnCancel. Поэтому, когда диалоговая панель содержит кнопки с идентификаторами IDOK и IDCANCEL, как правило, нет необходимости создавать для них обработчики.

Так как в таблице сообщений класса CDlg отсутствует макрокоманды для обработки сообщений от кнопок "Ok" и "Cancel", они передаются для обработки базовому классу CDialog. Здесь они обрабатываются виртуальными методами OnOK и OnCancel.

Метод OnOK, определенный в классе CDialog, копирует данные из полей диалоговой панели в связанные с ними переменные. Для этого вызывается метод UpdateData с параметром TRUE. Затем выполняется вызов метода EndDialog, который закрывает диалоговую панель и возвращает значение IDOK. Метод DoModal, который используется для создания диалоговой панели и вызывается в классе родительского окна, прекращает работу и возвращает IDOK.

Метод OnCancel, определенный в классе CDialog, еще проще, чем OnOK. Он только закрывает диалоговую панель и возвращает значение IDCANCEL. Копирование данных не происходит, так как пользователь отменил изменения, нажав кнопку "Cancel".

Так как методы OnOK и OnCancel определены в классе CDialog как виртуальные, то можно переопределить их в классе CDlg. В этом случае управление получат переопределенные методы, а не методы класса CDialog. Методы класса можно вызвать, явно указав класс CDialog.



Отображение модальной диалоговой панели



Для отображения модальной диалоговой панели сначала создается объект класса CDlg, который будет представлять диалоговую панель. Когда объект создан, диалоговая панель еще не появляется на экране, для этого нужно воспользоваться методом DoModal, определенным в классе CDialog. При вызове метода DoModal выполнение метода, вызвавшего модальный диалог, приостанавливается, пока пользователь не закроет диалоговую панель.

Для отображения модальной диалоговой панели обычно используется следующий код, размещаемый в методе класса приложения, в котором вызывается диалог (например, в методе-обработчике какого-либо сообщения для окна приложения):

#include “dlg.h” …… // Создание объекта класса диалога CDlg Dlg; // при необходимости можно в конструктор класса диалога // передать указатель на родительское окно, по умолчанию // диалог является модальным ко всему приложению int result; // Mожно проинициировать переменные класса Dlg.m_Str=”Введите текст”; // Проверка возвращаемого методом DoModal значения if((result=Dlg.DoModal())==IDOK) { // Код, который вызывается, если пользователь нажимает кнопку "OK" ……… // Можно воспользоваться данными, полученными в процессе диалога AfxMessageBox(MyDialog.m_Str); } else if(result==IDCANCEL) { // Код, который вызывается, если нажата кнопка "Cancel" ……… } ……


Модель COM


СОМ устанавливает абстракции и правила, необходимые для определения объектов и интерфейсов; в ее состав входит также программное обеспечение, реализующее ключевые функции. Хотя СОМ не является ни большой, ни слишком сложной, она определенно отличается как от других архитектур предоставления программных сервисов, так и от традиционных объектно-ориентированных подходов. Поэтому разработчикам придется взглянуть по-новому на, казалось бы, знакомые вещи.



Моникеры


Моникер (moniker, имя, кличка) сам по себе является СОМ-объектом, но весьма специфического назначения: любой моникер знает, как создать и инициализировать экземпляр другого объекта. Например, имея моникер для банковского счета, можно попросить его создать счет, инициализировать его и соединить с ним. Все детали, необходимые для выполнения этих действий, скрыты от клиента. Если он хочет работать посредством моникеров с двумя банковскими счетами, то ему потребуется два отдельных моникера, по одному для каждого объекта — счета. Вообще моникеры в среде СОМ не необходимы; они просто облегчают жизнь клиентов.



Multi.cpp


// multi.cpp : Defines the class behaviors for the application. //

#include "stdafx.h" #include "multi.h"

#include "MainFrm.h" #include "ChildFrm.h" #include "multiDoc.h" #include "multiView.h"

#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif

///////////////////////////////////////////////////////////////////////////// // CMultiApp

BEGIN_MESSAGE_MAP(CMultiApp, CWinApp) //{{AFX_MSG_MAP(CMultiApp) ON_COMMAND(ID_APP_ABOUT, OnAppAbout) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP // Standard file based document commands ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) // Standard print setup command ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup) END_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////////// // CMultiApp construction

CMultiApp::CMultiApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance }

///////////////////////////////////////////////////////////////////////////// // The one and only CMultiApp object

CMultiApp theApp;

///////////////////////////////////////////////////////////////////////////// // CMultiApp initialization

BOOL CMultiApp::InitInstance() { // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need.

#ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL #else Enable3dControlsStatic(); // Call this when linking to MFC statically #endif

// Change the registry key under which our settings are stored. // You should modify this string to be something appropriate // such as the name of your company or organization. SetRegistryKey(_T("Local AppWizard-Generated Applications"));


LoadStdProfileSettings(); // Load standard INI file options (including MRU)

// Register the application's document templates. Document templates // serve as the connection between documents, frame windows and views.

CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_MULTITYPE, RUNTIME_CLASS(CMultiDoc), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CMultiView)); AddDocTemplate(pDocTemplate);

// create main MDI Frame window CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE; m_pMainWnd = pMainFrame;

// Parse command line for standard shell commands, DDE, file open CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo);

// Dispatch commands specified on the command line if (!ProcessShellCommand(cmdInfo)) return FALSE;

// The main window has been initialized, so show and update it. pMainFrame->ShowWindow(m_nCmdShow); pMainFrame->UpdateWindow();

return TRUE; }

///////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About

class CAboutDlg : public CDialog { public: CAboutDlg();

// Dialog Data //{{AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; //}}AFX_DATA

// ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CAboutDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL

// Implementation protected: //{{AFX_MSG(CAboutDlg) // No message handlers //}}AFX_MSG DECLARE_MESSAGE_MAP() };

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { //{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT }

void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) //}}AFX_DATA_MAP }

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //{{AFX_MSG_MAP(CAboutDlg) // No message handlers //}}AFX_MSG_MAP END_MESSAGE_MAP()

// App command to run the dialog void CMultiApp::OnAppAbout() { CAboutDlg aboutDlg; aboutDlg.DoModal(); }

///////////////////////////////////////////////////////////////////////////// // CMultiApp commands


Multi.h


// multi.h : main header file for the MULTI application //

#if !defined(AFX_MULTI_H__A7203688_E01B_11D1_9525_0080488929D2__INCLUDED_) #define AFX_MULTI_H__A7203688_E01B_11D1_9525_0080488929D2__INCLUDED_

#if _MSC_VER >= 1000 #pragma once #endif // _MSC_VER >= 1000

#ifndef __AFXWIN_H__ #error include 'stdafx.h' before including this file for PCH #endif

#include "resource.h" // main symbols

///////////////////////////////////////////////////////////////////////////// // CMultiApp: // See multi.cpp for the implementation of this class //

class CMultiApp : public CWinApp { public: CMultiApp();

// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CMultiApp) public: virtual BOOL InitInstance(); //}}AFX_VIRTUAL

// Implementation

//{{AFX_MSG(CMultiApp) afx_msg void OnAppAbout(); // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP() };

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}} // Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MULTI_H__A7203688_E01B_11D1_9525_0080488929D2__INCLUDED_)



Multi.rc


//Microsoft Visual C++ generated resource script. // #include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. //

#include "afxres.h"

///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS

#ifdef APSTUDIO_INVOKED

///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE //

1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END

2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END

3 TEXTINCLUDE DISCARDABLE BEGIN "#define _AFX_NO_SPLITTER_RESOURCES\r\n" "#define _AFX_NO_OLE_RESOURCES\r\n" "#define _AFX_NO_TRACKER_RESOURCES\r\n" "#define _AFX_NO_PROPERTY_RESOURCES\r\n" "\r\n" "#if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_ENU)\r\n" "#ifdef _WIN32\r\n" "LANGUAGE 9, 1\r\n" "#pragma code_page(1252)\r\n" "#endif\r\n" "#include ""res\\multi.rc2"" // non-Microsoft Visual C++ edited resources\r\n" "#include ""afxres.rc"" // Standard components\r\n" "#include ""afxprint.rc"" // printing/print preview resources\r\n" "#endif" "\0" END

#endif // APSTUDIO_INVOKED

///////////////////////////////////////////////////////////////////////////// // // Icon //

// Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems.

#if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE 9, 1 #pragma code_page(1252) #endif IDR_MAINFRAME ICON DISCARDABLE "res\\multi.ico" IDR_MULTITYPE ICON DISCARDABLE "res\\multiDoc.ico" #endif

///////////////////////////////////////////////////////////////////////////// // // Bitmap //


IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp"

///////////////////////////////////////////////////////////////////////////// // // Toolbar //

IDR_MAINFRAME TOOLBAR DISCARDABLE 16, 15 BEGIN BUTTON ID_FILE_NEW BUTTON ID_FILE_OPEN BUTTON ID_FILE_SAVE SEPARATOR BUTTON ID_EDIT_CUT BUTTON ID_EDIT_COPY BUTTON ID_EDIT_PASTE SEPARATOR BUTTON ID_FILE_PRINT BUTTON ID_APP_ABOUT END

#if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE 9, 1 #pragma code_page(1252) #endif ///////////////////////////////////////////////////////////////////////////// // // Menu //

IDR_MAINFRAME MENU PRELOAD DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "&New\tCtrl+N", ID_FILE_NEW MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN MENUITEM SEPARATOR MENUITEM "P&rint Setup...", ID_FILE_PRINT_SETUP MENUITEM SEPARATOR MENUITEM "Recent File", ID_FILE_MRU_FILE1,GRAYED MENUITEM SEPARATOR MENUITEM "E&xit", ID_APP_EXIT END POPUP "&View" BEGIN MENUITEM "&Toolbar", ID_VIEW_TOOLBAR MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR END POPUP "&Help" BEGIN MENUITEM "&About multi...", ID_APP_ABOUT END END IDR_MULTITYPE MENU PRELOAD DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "&New\tCtrl+N", ID_FILE_NEW MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN MENUITEM "&Close", ID_FILE_CLOSE MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE MENUITEM "Save &As...", ID_FILE_SAVE_AS MENUITEM SEPARATOR MENUITEM "&Print...\tCtrl+P", ID_FILE_PRINT MENUITEM "Print Pre&view", ID_FILE_PRINT_PREVIEW MENUITEM "P&rint Setup...", ID_FILE_PRINT_SETUP MENUITEM SEPARATOR MENUITEM "Recent File", ID_FILE_MRU_FILE1,GRAYED MENUITEM SEPARATOR MENUITEM "E&xit", ID_APP_EXIT END POPUP "&Edit" BEGIN MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO MENUITEM SEPARATOR MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE END POPUP "&View" BEGIN MENUITEM "&Toolbar", ID_VIEW_TOOLBAR MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR END POPUP "&Window" BEGIN MENUITEM "&New Window", ID_WINDOW_NEW MENUITEM "&Cascade", ID_WINDOW_CASCADE MENUITEM "&Tile", ID_WINDOW_TILE_HORZ MENUITEM "&Arrange Icons", ID_WINDOW_ARRANGE END POPUP "&Help" BEGIN MENUITEM "&About multi...", ID_APP_ABOUT END END



///////////////////////////////////////////////////////////////////////////// // // Accelerator //

IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE BEGIN "N", ID_FILE_NEW, VIRTKEY,CONTROL "O", ID_FILE_OPEN, VIRTKEY,CONTROL "S", ID_FILE_SAVE, VIRTKEY,CONTROL "P", ID_FILE_PRINT, VIRTKEY,CONTROL "Z", ID_EDIT_UNDO, VIRTKEY,CONTROL "X", ID_EDIT_CUT, VIRTKEY,CONTROL "C", ID_EDIT_COPY, VIRTKEY,CONTROL "V", ID_EDIT_PASTE, VIRTKEY,CONTROL VK_BACK, ID_EDIT_UNDO, VIRTKEY,ALT VK_DELETE, ID_EDIT_CUT, VIRTKEY,SHIFT VK_INSERT, ID_EDIT_COPY, VIRTKEY,CONTROL VK_INSERT, ID_EDIT_PASTE, VIRTKEY,SHIFT VK_F6, ID_NEXT_PANE, VIRTKEY VK_F6, ID_PREV_PANE, VIRTKEY,SHIFT END

///////////////////////////////////////////////////////////////////////////// // // Dialog //

IDD_ABOUTBOX DIALOG DISCARDABLE 0, 0, 217, 55 CAPTION "About multi" STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU FONT 8, "MS Sans Serif" BEGIN ICON IDR_MAINFRAME,IDC_STATIC,11,17,20,20 LTEXT "multi Version 1.0",IDC_STATIC,40,10,119,8, SS_NOPREFIX LTEXT "Copyright (C) 1998",IDC_STATIC,40,25,119,8 DEFPUSHBUTTON "OK",IDOK,178,7,32,14,WS_GROUP END

///////////////////////////////////////////////////////////////////////////// // // Version //

VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,1 PRODUCTVERSION 1,0,0,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x1L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904B0" BEGIN VALUE "CompanyName", "\0" VALUE "FileDescription", "multi MFC Application\0" VALUE "FileVersion", "1, 0, 0, 1\0" VALUE "InternalName", "multi\0" VALUE "LegalCopyright", "Copyright (C) 1998\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename","multi.EXE\0" VALUE "ProductName", "multi Application\0" VALUE "ProductVersion", "1, 0, 0, 1\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END



///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO //

# ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN IDD_ABOUTBOX, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 210 TOPMARGIN, 7 BOTTOMMARGIN, 48 END END #endif // APSTUDIO_INVOKED

///////////////////////////////////////////////////////////////////////////// // // String Table //

STRINGTABLE PRELOAD DISCARDABLE BEGIN IDR_MAINFRAME "multi" IDR_MULTITYPE "\nMulti\nMulti\n\n\nMulti.Document\nMulti Document" END STRINGTABLE PRELOAD DISCARDABLE BEGIN AFX_IDS_APP_TITLE "multi" AFX_IDS_IDLEMESSAGE "Ready" END STRINGTABLE DISCARDABLE BEGIN ID_INDICATOR_EXT "EXT" ID_INDICATOR_CAPS "CAP" ID_INDICATOR_NUM "NUM" ID_INDICATOR_SCRL "SCRL" ID_INDICATOR_OVR "OVR" ID_INDICATOR_REC "REC" END STRINGTABLE DISCARDABLE BEGIN ID_FILE_NEW "Create a new document\nNew" ID_FILE_OPEN "Open an existing document\nOpen" ID_FILE_CLOSE "Close the active document\nClose" ID_FILE_SAVE "Save the active document\nSave" ID_FILE_SAVE_AS "Save the active document with a new name\nSave As" ID_FILE_PAGE_SETUP "Change the printing options\nPage Setup" ID_FILE_PRINT_SETUP "Change the printer and printing options\nPrint Setup" ID_FILE_PRINT "Print the active document\nPrint" ID_FILE_PRINT_PREVIEW "Display full pages\nPrint Preview" ID_APP_ABOUT "Display program information, version number and copyright\nAbout" ID_APP_EXIT "Quit the application; prompts to save documents\nExit" ID_FILE_MRU_FILE1 "Open this document" ID_FILE_MRU_FILE2 "Open this document" ID_FILE_MRU_FILE3 "Open this document" ID_FILE_MRU_FILE4 "Open this document" ID_FILE_MRU_FILE5 "Open this document" ID_FILE_MRU_FILE6 "Open this document" ID_FILE_MRU_FILE7 "Open this document" ID_FILE_MRU_FILE8 "Open this document" ID_FILE_MRU_FILE9 "Open this document" ID_FILE_MRU_FILE10 "Open this document" ID_FILE_MRU_FILE11 "Open this document" ID_FILE_MRU_FILE12 "Open this document" ID_FILE_MRU_FILE13 "Open this document" ID_FILE_MRU_FILE14 "Open this document" ID_FILE_MRU_FILE15 "Open this document" ID_FILE_MRU_FILE16 "Open this document" ID_NEXT_PANE "Switch to the next window pane\nNext Pane" ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane" ID_WINDOW_NEW "Open another window for the active document\nNew Window" ID_WINDOW_ARRANGE "Arrange icons at the bottom of the window\nArrange Icons" ID_WINDOW_CASCADE "Arrange windows so they overlap\nCascade Windows" ID_WINDOW_TILE_HORZ "Arrange windows as non-overlapping tiles\nTile Windows" ID_WINDOW_TILE_VERT "Arrange windows as non-overlapping tiles\nTile Windows" ID_WINDOW_SPLIT "Split the active window into panes\nSplit" ID_EDIT_CLEAR "Erase the selection\nErase" ID_EDIT_CLEAR_ALL "Erase everything\nErase All" ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy" ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" ID_EDIT_FIND "Find the specified text\nFind" ID_EDIT_PASTE "Insert Clipboard contents\nPaste" ID_EDIT_REPEAT "Repeat the last action\nRepeat" ID_EDIT_REPLACE "Replace specific text with different text\nReplace" ID_EDIT_SELECT_ALL "Select the entire document\nSelect All" ID_EDIT_UNDO "Undo the last action\nUndo" ID_EDIT_REDO "Redo the previously undone action\nRedo" ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle ToolBar" ID_VIEW_STATUS_BAR "Show or hide the status bar\nToggle StatusBar" END



STRINGTABLE DISCARDABLE BEGIN AFX_IDS_SCSIZE " Change the window size" AFX_IDS_SCMOVE "Change the window position" AFX_IDS_SCMINIMIZE "Reduce the window to an icon" AFX_IDS_SCMAXIMIZE "Enlarge the window to full size" AFX_IDS_SCNEXTWINDOW "Switch to the next document window" AFX_IDS_SCPREVWINDOW "Switch to the previous document window" AFX_IDS_SCCLOSE "Close the active window and prompts to save the documents" AFX_IDS_SCRESTORE "Restore the window to normal size" AFX_IDS_SCTASKLIST "Activate Task List" AFX_IDS_MDICHILD "Activate this window" AFX_IDS_PREVIEW_CLOSE "Close print preview mode\nCancel Preview" END

#endif

#ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. //

#define _AFX_NO_SPLITTER_RESOURCES #define _AFX_NO_OLE_RESOURCES #define _AFX_NO_TRACKER_RESOURCES #define _AFX_NO_PROPERTY_RESOURCES

#if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE 9, 1 #pragma code_page(1252) #endif #include "res\\multi.rc2" // non-Microsoft Visual C++ edited resources #include "afxres.rc" // Standard components #include "afxprint.rc" // printing/print preview resources #endif

#endif // not APSTUDIO_INVOKED


MultiDoc.cpp


// multiDoc.cpp : implementation of the CMultiDoc class //

#include "stdafx.h" #include "multi.h"

#include "multiDoc.h"

#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif

///////////////////////////////////////////////////////////////////////////// // CMultiDoc

IMPLEMENT_DYNCREATE(CMultiDoc, CDocument)

BEGIN_MESSAGE_MAP(CMultiDoc, CDocument) //{{AFX_MSG_MAP(CMultiDoc) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP END_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////////// // CMultiDoc construction/destruction

CMultiDoc::CMultiDoc() { // TODO: add one-time construction code here

}

CMultiDoc::~CMultiDoc() { }

BOOL CMultiDoc::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE;

// TODO: add reinitialization code here // (SDI documents will reuse this document)

return TRUE; }

///////////////////////////////////////////////////////////////////////////// // CMultiDoc serialization

void CMultiDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO: add storing code here } else { // TODO: add loading code here } }

///////////////////////////////////////////////////////////////////////////// // CMultiDoc diagnostics

#ifdef _DEBUG void CMultiDoc::AssertValid() const { CDocument::AssertValid(); }

void CMultiDoc::Dump(CDumpContext& dc) const { CDocument::Dump(dc); } #endif //_DEBUG

///////////////////////////////////////////////////////////////////////////// // CMultiDoc commands



MultiDoc.h


// multiDoc.h : interface of the CMultiDoc class // /////////////////////////////////////////////////////////////////////////////

#if !defined(AFX_MULTIDOC_H__A7203690_E01B_11D1_9525_0080488929D2__INCLUDED_) #define AFX_MULTIDOC_H__A7203690_E01B_11D1_9525_0080488929D2__INCLUDED_

#if _MSC_VER >= 1000 #pragma once #endif // _MSC_VER >= 1000

class CMultiDoc : public CDocument { protected: // create from serialization only CMultiDoc(); DECLARE_DYNCREATE(CMultiDoc)

// Attributes public:

// Operations public:

// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CMultiDoc) public: virtual BOOL OnNewDocument(); virtual void Serialize(CArchive& ar); //}}AFX_VIRTUAL

// Implementation public: virtual ~CMultiDoc(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif

protected:

// Generated message map functions protected: //{{AFX_MSG(CMultiDoc) // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP() };

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}} // Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MULTIDOC_H__A7203690_E01B_11D1_9525_0080488929D2__INCLUDED_)



MultiView.cpp


// multiView.cpp : implementation of the CMultiView class //

#include "stdafx.h" #include "multi.h"

#include "multiDoc.h" #include "multiView.h"

#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif

///////////////////////////////////////////////////////////////////////////// // CMultiView

IMPLEMENT_DYNCREATE(CMultiView, CView)

BEGIN_MESSAGE_MAP(CMultiView, CView) //{{AFX_MSG_MAP(CMultiView) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP // Standard printing commands ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) END_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////////// // CMultiView construction/destruction

CMultiView::CMultiView() { // TODO: add construction code here

}

CMultiView::~CMultiView() { }

BOOL CMultiView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs

return CView::PreCreateWindow(cs); }

///////////////////////////////////////////////////////////////////////////// // CMultiView drawing

void CMultiView::OnDraw(CDC* pDC) { CMultiDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc);

// TODO: add draw code for native data here }

///////////////////////////////////////////////////////////////////////////// // CMultiView printing

BOOL CMultiView::OnPreparePrinting(CPrintInfo* pInfo) { // default preparation return DoPreparePrinting(pInfo); }

void CMultiView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add extra initialization before printing }

void CMultiView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add cleanup after printing }

///////////////////////////////////////////////////////////////////////////// // CMultiView diagnostics

#ifdef _DEBUG void CMultiView::AssertValid() const { CView::AssertValid(); }

void CMultiView::Dump(CDumpContext& dc) const { CView::Dump(dc); }

CMultiDoc* CMultiView::GetDocument() // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMultiDoc))); return (CMultiDoc*)m_pDocument; } #endif //_DEBUG

///////////////////////////////////////////////////////////////////////////// // CMultiView message handlers



MultiView.h


// multiView.h : interface of the CMultiView class // /////////////////////////////////////////////////////////////////////////////

#if !defined(AFX_MULTIVIEW_H__A7203692_E01B_11D1_9525_0080488929D2__INCLUDED_) #define AFX_MULTIVIEW_H__A7203692_E01B_11D1_9525_0080488929D2__INCLUDED_

#if _MSC_VER >= 1000 #pragma once #endif // _MSC_VER >= 1000

class CMultiView : public CView { protected: // create from serialization only CMultiView(); DECLARE_DYNCREATE(CMultiView)

// Attributes public: CMultiDoc* GetDocument();

// Operations public:

// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CMultiView) public: virtual void OnDraw(CDC* pDC); // overridden to draw this view virtual BOOL PreCreateWindow(CREATESTRUCT& cs); protected: virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); //}}AFX_VIRTUAL

// Implementation public: virtual ~CMultiView(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif

protected:

// Generated message map functions protected: //{{AFX_MSG(CMultiView) // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP() };

#ifndef _DEBUG // debug version in multiView.cpp inline CMultiDoc* CMultiView::GetDocument() { return (CMultiDoc*)m_pDocument; } #endif

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}} // Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MULTIVIEW_H__A7203692_E01B_11D1_9525_0080488929D2__INCLUDED_)



Name.cpp


// name.cpp : Implementation of CNameApp and DLL registration.   #include "stdafx.h" #include "name.h"   #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif   CNameApp NEAR theApp;   const GUID CDECL BASED_CODE _tlid = { 0xec72d07a, 0xfabf, 0x11d1, { 0x95, 0x25, 0, 0x80, 0x48, 0x89, 0x29, 0xd2 } }; const WORD _wVerMajor = 1; const WORD _wVerMinor = 0;   //////////////////////////////////////////////////////////////////////////// // CNameApp::InitInstance - DLL initialization BOOL CNameApp::InitInstance() { BOOL bInit = COleControlModule::InitInstance();   if (bInit) { // TODO: Add your own module initialization code here. } return bInit; }   //////////////////////////////////////////////////////////////////////////// // CNameApp::ExitInstance - DLL termination int CNameApp::ExitInstance() { // TODO: Add your own module termination code here.   return COleControlModule::ExitInstance(); }   ///////////////////////////////////////////////////////////////////////////// // DllRegisterServer - Adds entries to the system registry   STDAPI DllRegisterServer(void) { AFX_MANAGE_STATE(_afxModuleAddrThis);   if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid)) return ResultFromScode(SELFREG_E_TYPELIB);   if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE)) return ResultFromScode(SELFREG_E_CLASS);   return NOERROR; }   ///////////////////////////////////////////////////////////////////////////// // DllUnregisterServer - Removes entries from the system registry   STDAPI DllUnregisterServer(void) { AFX_MANAGE_STATE(_afxModuleAddrThis);   if (!AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor)) return ResultFromScode(SELFREG_E_TYPELIB);   if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE)) return ResultFromScode(SELFREG_E_CLASS);   return NOERROR; }



Name.h


#if !defined(AFX_NAME_H__EC72D082_FABF_11D1_9525_0080488929D2__INCLUDED_) #define AFX_NAME_H__EC72D082_FABF_11D1_9525_0080488929D2__INCLUDED_ #if _MSC_VER >= 1000 #pragma once #endif // _MSC_VER >= 1000   // name.h : main header file for NAME.DLL   #if !defined( __AFXCTL_H__ ) #error include 'afxctl.h' before including this file #endif   #include "resource.h" // main symbols   ///////////////////////////////////////////////////////////////////////////// // CNameApp : See name.cpp for implementation.   class CNameApp : public COleControlModule { public: BOOL InitInstance(); int ExitInstance(); };   extern const GUID CDECL _tlid; extern const WORD _wVerMajor; extern const WORD _wVerMinor;   //{{AFX_INSERT_LOCATION}} // Microsoft Developer Studio will insert additional declarations immediately before the previous line.   #endif // !defined(AFX_NAME_H__EC72D082_FABF_11D1_9525_0080488929D2__INCLUDED)



Name.odl


// name.odl : type library source for ActiveX Control project.   // This file will be processed by the Make Type Library (mktyplib) tool to // produce the type library (name.tlb) that will become a resource in // name.ocx.   #include <olectl.h> #include <idispids.h>   [ uuid(EC72D07A-FABF-11D1-9525-0080488929D2), version(1.0), helpfile("name.hlp"), helpstring("name ActiveX Control module"), control ] library NAMELib { importlib(STDOLE_TLB); importlib(STDTYPE_TLB);   // Primary dispatch interface for CNameCtrl   [ uuid(EC72D07B-FABF-11D1-9525-0080488929D2), helpstring("Dispatch interface for Name Control"), hidden ] dispinterface _DName { properties: // NOTE - ClassWizard will maintain property information here. // Use extreme caution when editing this section. //{{AFX_ODL_PROP(CNameCtrl) //}}AFX_ODL_PROP   methods: // NOTE - ClassWizard will maintain method information here. // Use extreme caution when editing this section. //{{AFX_ODL_METHOD(CNameCtrl) //}}AFX_ODL_METHOD   [id(DISPID_ABOUTBOX)] void AboutBox(); };   // Event dispatch interface for CNameCtrl   [ uuid(EC72D07C-FABF-11D1-9525-0080488929D2), helpstring("Event interface for Name Control") ] dispinterface _DNameEvents { properties: // Event interface has no properties   methods: // NOTE - ClassWizard will maintain event information here. // Use extreme caution when editing this section. //{{AFX_ODL_EVENT(CNameCtrl) //}}AFX_ODL_EVENT };   // Class information for CNameCtrl   [ uuid(B85D087C-F9FA-11D1-9525-0080488929D2), helpstring("Name Control"), control ] coclass Name { [default] dispinterface _DName; [default, source] dispinterface _DNameEvents; };   //{{AFX_APPEND_ODL}} //}}AFX_APPEND_ODL}} };



Name.rc


//Microsoft Developer Studio generated resource script. // #include "resource.h"   #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h"   ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS   ///////////////////////////////////////////////////////////////////////////// // Russian resources   #if !defined(AFX_RESOURCE_DLL) defined(AFX_TARG_RUS) #ifdef _WIN32 LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT #pragma code_page(1251) #endif //_WIN32   #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // TEXTINCLUDE //   1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END   2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END   3 TEXTINCLUDE DISCARDABLE BEGIN "1 TYPELIB ""name.tlb""\r\n" "\0" END   #endif // APSTUDIO_INVOKED   #ifndef _MAC ///////////////////////////////////////////////////////////////////////////// // Version //   VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,1 PRODUCTVERSION 1,0,0,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904B0" BEGIN VALUE "CompanyName", "ВГУЭС" VALUE "FileDescription", "name ActiveX Control Module\0" VALUE "FileVersion", "1, 0, 0, 1\0" VALUE "InternalName", "NAME\0" VALUE "LegalCopyright", "Copyright (C) 1998\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "NAME.OCX\0" VALUE "ProductName", "name ActiveX Control Module\0" VALUE "ProductVersion", "1, 0, 0, 1\0" VALUE "OLESelfRegister", "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END   #endif // !_MAC   ///////////////////////////////////////////////////////////////////////////// // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems.
IDI_ABOUTDLL ICON DISCARDABLE "name.ico"   ///////////////////////////////////////////////////////////////////////////// // Bitmap // IDB_NAME BITMAP DISCARDABLE "NameCtl.bmp"   ///////////////////////////////////////////////////////////////////////////// // Dialog // IDD_ABOUTBOX_NAME DIALOG DISCARDABLE 34, 22, 260, 55 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About Name Control" FONT 8, "MS Sans Serif" BEGIN ICON IDI_ABOUTDLL,IDC_STATIC,10,10,20,20 LTEXT "Name Control, Version 1.0",IDC_STATIC,40,10,170,8 LTEXT "Copyright (C) 1998, КТС",IDC_STATIC,40,25,170,8 DEFPUSHBUTTON "OK",IDOK,221,7,32,14,WS_GROUP END   IDD_PROPPAGE_NAME DIALOG DISCARDABLE 0, 0, 250, 62 STYLE WS_CHILD FONT 8, "MS Sans Serif" BEGIN LTEXT "TODO: Place controls to manipulate properties of Name Control on this dialog.", IDC_STATIC,7,25,229,16 END   ///////////////////////////////////////////////////////////////////////////// // DESIGNINFO //   #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN IDD_ABOUTBOX_NAME, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 253 TOPMARGIN, 7 BOTTOMMARGIN, 48 END IDD_PROPPAGE_NAME, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 243 TOPMARGIN, 7 BOTTOMMARGIN, 55 END END #endif // APSTUDIO_INVOKED   ///////////////////////////////////////////////////////////////////////////// // String Table //   STRINGTABLE DISCARDABLE BEGIN IDS_NAME "Name Control" IDS_NAME_PPG "Name Property Page" END   STRINGTABLE DISCARDABLE BEGIN IDS_NAME_PPG_CAPTION "General" END   #endif // Russian resources /////////////////////////////////////////////////////////////////////////////   #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // Generated from the TEXTINCLUDE 3 resource. // 1 TYPELIB "name.tlb"   ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED


NameCtl.cpp


// NameCtl.cpp : Implementation of the CNameCtrl ActiveX Control class.   #include "stdafx.h" #include "name.h" #include "NameCtl.h" #include "NamePpg.h"   #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif   IMPLEMENT_DYNCREATE(CNameCtrl, COleControl)   ///////////////////////////////////////////////////////////////////////////// // Message map   BEGIN_MESSAGE_MAP(CNameCtrl, COleControl) //{{AFX_MSG_MAP(CNameCtrl) // NOTE - ClassWizard will add and remove message map entries // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG_MAP ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties) END_MESSAGE_MAP()   ///////////////////////////////////////////////////////////////////////////// // Dispatch map BEGIN_DISPATCH_MAP(CNameCtrl, COleControl) //{{AFX_DISPATCH_MAP(CNameCtrl) // NOTE - ClassWizard will add and remove dispatch map entries // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_DISPATCH_MAP DISP_FUNCTION_ID(CNameCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE) END_DISPATCH_MAP()   ///////////////////////////////////////////////////////////////////////////// // Event map BEGIN_EVENT_MAP(CNameCtrl, COleControl) //{{AFX_EVENT_MAP(CNameCtrl) // NOTE - ClassWizard will add and remove event map entries // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_EVENT_MAP END_EVENT_MAP()   ///////////////////////////////////////////////////////////////////////////// // Property pages   // TODO: Add more property pages as needed. Remember to increase the count! BEGIN_PROPPAGEIDS(CNameCtrl, 1) PROPPAGEID(CNamePropPage::guid) END_PROPPAGEIDS(CNameCtrl)   ///////////////////////////////////////////////////////////////////////////// // Initialize class factory and guid IMPLEMENT_OLECREATE_EX(CNameCtrl, "NAME.NameCtrl.1", 0xb85d087c, 0xf9fa, 0x11d1, 0x95, 0x25, 0, 0x80, 0x48, 0x89, 0x29, 0xd2)   ///////////////////////////////////////////////////////////////////////////// // Type library ID and version IMPLEMENT_OLETYPELIB(CNameCtrl, _tlid, _wVerMajor, _wVerMinor)   ///////////////////////////////////////////////////////////////////////////// // Interface IDs   const IID BASED_CODE IID_DName = { 0xec72d07b, 0xfabf, 0x11d1, { 0x95, 0x25, 0, 0x80, 0x48, 0x89, 0x29, 0xd2 } }; const IID BASED_CODE IID_DNameEvents = { 0xec72d07c, 0xfabf, 0x11d1, { 0x95, 0x25, 0, 0x80, 0x48, 0x89, 0x29, 0xd2 } };   ///////////////////////////////////////////////////////////////////////////// // Control type information static const DWORD BASED_CODE _dwNameOleMisc = OLEMISC_ACTIVATEWHENVISIBLE | OLEMISC_SETCLIENTSITEFIRST | OLEMISC_INSIDEOUT | OLEMISC_CANTLINKINSIDE | OLEMISC_RECOMPOSEONRESIZE;   IMPLEMENT_OLECTLTYPE(CNameCtrl, IDS_NAME, _dwNameOleMisc)   ///////////////////////////////////////////////////////////////////////////// // CNameCtrl::CNameCtrlFactory::UpdateRegistry - // Adds or removes system registry entries for CNameCtrl   BOOL CNameCtrl::CNameCtrlFactory::UpdateRegistry(BOOL bRegister) { // TODO: Verify that your control follows apartment-model threading rules. // Refer to MFC TechNote 64 for more information. // If your control does not conform to the apartment-model rules, then // you must modify the code below, changing the 6th parameter from // afxRegApartmentThreading to 0.   if (bRegister) return AfxOleRegisterControlClass( AfxGetInstanceHandle(), m_clsid, m_lpszProgID, IDS_NAME, IDB_NAME, afxRegApartmentThreading, _dwNameOleMisc, _tlid, _wVerMajor, _wVerMinor); else return AfxOleUnregisterClass(m_clsid, m_lpszProgID); }   ///////////////////////////////////////////////////////////////////////////// // CNameCtrl::CNameCtrl - Constructor   CNameCtrl::CNameCtrl() { InitializeIIDs(&IID_DName, &IID_DNameEvents);   // TODO: Initialize your control's instance data here. }   ///////////////////////////////////////////////////////////////////////////// // CNameCtrl::~CNameCtrl - Destructor   CNameCtrl::~CNameCtrl() { // TODO: Cleanup your control's instance data here. }   ///////////////////////////////////////////////////////////////////////////// // CNameCtrl::OnDraw - Drawing function   void CNameCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid) { // TODO: Replace the following code with your own drawing code.
pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH))); pdc->Ellipse(rcBounds); }   ///////////////////////////////////////////////////////////////////////////// // CNameCtrl::DoPropExchange - Persistence support   void CNameCtrl::DoPropExchange(CPropExchange* pPX) { ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor)); COleControl::DoPropExchange(pPX);   // TODO: Call PX_ functions for each persistent custom property.   }   ///////////////////////////////////////////////////////////////////////////// // CNameCtrl::OnResetState - Reset control to default state   void CNameCtrl::OnResetState() { COleControl::OnResetState(); // Resets defaults found in DoPropExchange   // TODO: Reset any other control state here. }   ///////////////////////////////////////////////////////////////////////////// // CNameCtrl::AboutBox - Display an "About" box to the user   void CNameCtrl::AboutBox() { CDialog dlgAbout(IDD_ABOUTBOX_NAME); dlgAbout.DoModal(); }   ///////////////////////////////////////////////////////////////////////////// // CNameCtrl message handlers


NameCtl.h


#if !defined(AFX_NAMECTL_H__EC72D08A_FABF_11D1_9525_0080488929D2__INCLUDED_) #define AFX_NAMECTL_H__EC72D08A_FABF_11D1_9525_0080488929D2__INCLUDED_ #if _MSC_VER >= 1000 #pragma once #endif // _MSC_VER >= 1000   // NameCtl.h : Declaration of the CNameCtrl ActiveX Control class.   ///////////////////////////////////////////////////////////////////////////// // CNameCtrl : See NameCtl.cpp for implementation.   class CNameCtrl : public COleControl { DECLARE_DYNCREATE(CNameCtrl)   // Constructor public: CNameCtrl();   // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CNameCtrl) public: virtual void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid); virtual void DoPropExchange(CPropExchange* pPX); virtual void OnResetState(); //}}AFX_VIRTUAL   // Implementation protected: ~CNameCtrl();   DECLARE_OLECREATE_EX(CNameCtrl) // Class factory and guid DECLARE_OLETYPELIB(CNameCtrl) // GetTypeInfo DECLARE_PROPPAGEIDS(CNameCtrl) // Property page IDs DECLARE_OLECTLTYPE(CNameCtrl) // Type name and misc status   // Message maps //{{AFX_MSG(CNameCtrl) // NOTE - ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP()   // Dispatch maps //{{AFX_DISPATCH(CNameCtrl) // NOTE - ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_DISPATCH DECLARE_DISPATCH_MAP()   afx_msg void AboutBox();   // Event maps //{{AFX_EVENT(CNameCtrl) // NOTE - ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_EVENT DECLARE_EVENT_MAP()   // Dispatch and event IDs public: enum { //{{AFX_DISP_ID(CNameCtrl) // NOTE: ClassWizard will add and remove enumeration elements here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_DISP_ID }; };   //{{AFX_INSERT_LOCATION}} // Microsoft Developer Studio will insert additional declarations immediately before the previous line.   #endif // !defined(AFX_NAMECTL_H__EC72D08A_FABF_11D1_9525_0080488929D2__INCLUDED)



NamePpg.cpp


// NamePpg.cpp : Implementation of the CNamePropPage property page class.   #include "stdafx.h" #include "name.h" #include "NamePpg.h"   #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif   IMPLEMENT_DYNCREATE(CNamePropPage, COlePropertyPage)   ///////////////////////////////////////////////////////////////////////////// // Message map BEGIN_MESSAGE_MAP(CNamePropPage, COlePropertyPage) //{{AFX_MSG_MAP(CNamePropPage) // NOTE - ClassWizard will add and remove message map entries // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG_MAP END_MESSAGE_MAP()   ///////////////////////////////////////////////////////////////////////////// // Initialize class factory and guid IMPLEMENT_OLECREATE_EX(CNamePropPage, "NAME.NamePropPage.1", 0xec72d07d, 0xfabf, 0x11d1, 0x95, 0x25, 0, 0x80, 0x48, 0x89, 0x29, 0xd2)   ///////////////////////////////////////////////////////////////////////////// // CNamePropPage::CNamePropPageFactory::UpdateRegistry - // Adds or removes system registry entries for CNamePropPage   BOOL CNamePropPage::CNamePropPageFactory::UpdateRegistry(BOOL bRegister) { if (bRegister) return AfxOleRegisterPropertyPageClass(AfxGetInstanceHandle(), m_clsid, IDS_NAME_PPG); else return AfxOleUnregisterClass(m_clsid, NULL); }   ///////////////////////////////////////////////////////////////////////////// // CNamePropPage::CNamePropPage - Constructor   CNamePropPage::CNamePropPage() : COlePropertyPage(IDD, IDS_NAME_PPG_CAPTION) { //{{AFX_DATA_INIT(CNamePropPage) // NOTE: ClassWizard will add member initialization here // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_DATA_INIT }   ///////////////////////////////////////////////////////////////////////////// // CNamePropPage::DoDataExchange - Moves data between page and properties   void CNamePropPage::DoDataExchange(CDataExchange* pDX) { //{{AFX_DATA_MAP(CNamePropPage) // NOTE: ClassWizard will add DDP, DDX, and DDV calls here // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_DATA_MAP DDP_PostProcessing(pDX); }   ///////////////////////////////////////////////////////////////////////////// // CNamePropPage message handlers



NamePpg.h


#if !defined(AFX_NAMEPPG_H__EC72D08C_FABF_11D1_9525_0080488929D2__INCLUDED_) #define AFX_NAMEPPG_H__EC72D08C_FABF_11D1_9525_0080488929D2__INCLUDED_ #if _MSC_VER >= 1000 #pragma once #endif // _MSC_VER >= 1000   // NamePpg.h : Declaration of the CNamePropPage property page class.   //////////////////////////////////////////////////////////////////////////// // CNamePropPage : See NamePpg.cpp.cpp for implementation.   class CNamePropPage : public COlePropertyPage { DECLARE_DYNCREATE(CNamePropPage) DECLARE_OLECREATE_EX(CNamePropPage)   // Constructor public: CNamePropPage();   // Dialog Data //{{AFX_DATA(CNamePropPage) enum { IDD = IDD_PROPPAGE_NAME }; // NOTE - ClassWizard will add data members here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_DATA   // Implementation protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support   // Message maps protected: //{{AFX_MSG(CNamePropPage) // NOTE - ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP()   };   //{{AFX_INSERT_LOCATION}} // Microsoft Developer Studio will insert additional declarations immediately before the previous line.   #endif // !defined(AFX_NAMEPPG_H__EC72D08C_FABF_11D1_9525_0080488929D2__INCLUDED)



Некоторые методы класса CButton


UINT GetState() const;

Возвращает описание набора текущих состояний кнопки. Чтобы выделить из этого описания значения конкретных типов состояния, можно использовать маски:

0х0003 - выделяет собственное состояние кнопки. Применимо только к флажку или переключателю. Если результат побитового умножения дает 0, значит кнопка находится в невыбранном состоянии, 1 - в выбранном, 2 - в неопределенном.

0х0004 - выделяет состояние первого типа. Ненулевой вариант означает, что кнопка "нажата", нулевой - кнопка свободна.

0х0008 - выделяет положение фокуса. Ненулевой вариант - кнопка в фокусе клавиатуры.

int GetCheck() const;

Возвращает собственное состояние флажка или переключателя. Возвращаемое значение может принимать одно из значений: 0 - кнопка не выбрана; 1 - кнопка выбрана; 2 - кнопка в неопределенном состоянии. Если кнопка не является ни переключателем, ни флажком, возвращается 0.

void SetCheck(int nCheck);

Устанавливает собственное состояние флажка или переключателя. Значения задаются из набора: 0 - невыбранное; 1 - выбранное; 2 - неопределенное. Значение 2 применимо только к флажку со свойством 3State.

UINT GetButtonStyle() const;

Возвращает стиль кнопки.

void SetButtonStyle(UINT nStyle, BOOL bRedraw=TRUE);

Устанавливает стиль кнопки. Если параметр bRedraw равен TRUE, кнопка перерисовывается.

HICON GetIcon() const;

Возвращает дескриптор пиктограммы, сопоставленной кнопке. Если у кнопки нет сопоставленной пиктограммы, возвращает NULL.

HICON SetIcon(HICON hIcon);

Сопоставляет кнопке пиктограмму. Значением параметра при вызове должен быть дескриптор пиктограммы.

Пиктограмма автоматически помешается на поверхность кнопки и сдвигается в ее центр. Если поверхность кнопки меньше пиктограммы, она обрезается со всех сторон до размеров кнопки. Положение пиктограммы может быть выровнено и не по центру. Для этого нужно, чтобы кнопка имела одно из следующих свойств: BS_LEFT, BS_RIGHT, BS_CENTER, BS_TOP, BS_BOTTOM, BS_VCENTER

Данный метод устанавливает для кнопки только одну пиктограмму, которая будет наравне с текстом присутствовать при любом ее состоянии. Не надо путать ее с растровым изображением у растровой кнопки.

HBITMAP GetBitmap() const;

Возвращает дескриптор растрового изображения, сопоставленного кнопке. Если такового не существует, то возвращается NULL.

HBITMAP SetBitmap(HBITMAP hBitmap);

Сопоставляет кнопке растровое изображение. Значением параметра должен быть дескриптор растрового изображения. Правила размещения растрового изображения такие же, как и у значка.

HCURSOR GetCursor();

Возвращает дескриптор курсора, сопоставленного кнопке методом SetCursor. Если у кнопки нет сопоставленного курсора, то возвращается NULL.

HCURSOR SetCursor(HCURSOR hCursot);

Сопоставляет кнопке курсор, изображение которого будет помещено на поверхность кнопки аналогично значку и растровому изображению.



Некоторые методы класса CEdit


Окна редактирования могут работать в режимах однострочного и многострочного редакторов. Приведем сначала методы, общие для обоих режимов, а затем методы для многострочного редактора.

Общие методы

DWORD GetSel() const;

void GetSel(int& nStartChar, int& nEndChar) const;

Получает первую и последнюю позиции выделенного текста. Для значения типа DWORD младшее слово содержит позицию первого, старшее - последнего символа.

void SetSel(DWORD dwSelection, BOOL bNoScroll=FALSE);

void SetSel(int nStartChar, int nEndChar, BOOL bNoScroll=FALSE);

Устанавливает новое выделение текста, задавая первый и последний выделенный символ. Значение FALSE параметра bNoScroll должно отключать перемещение курсора в область видимости.

void ReplaceSel(LPCTSTR lpszNewText);

Заменяет выделенный текст на строку, передаваемую в параметре lpszNewText.

void Clear();

Удаляет выделенный текст.

void Copy();

Копирует выделенный текст в буфер.

void Cut();

Переносит (копирует и удаляет) выделенный текст в буфер обмена.

void Paste();

Вставляет текст из буфера обмена, начиная с позиции, в которой находится курсор.

BOOL Undo();

Отмена последней операции, выполненной редактором. Если редактор однострочный, возвращается всегда неотрицательное значение, иначе неотрицательное значение возвращается лишь в случае успешной замены.

BOOL CanUndo() const;

Определяет, можно ли отменить последнюю операцию редактора.

void EmptyUndoBuffer();

Сбрасывает флаг undo, сигнализирующий о возможности отмены последней операции редактора, и тем самым делает невозможным отмену. Этот флаг сбрасывается автоматически при выполнении методов SetWindowText и SetHandle.

BOOL GetModify() const;

Возвращает неотрицательное значение, если содержимое окна редактирования не модифицировалось. Информация о модификации поддерживается в специальном флаге, обнуляемом при создании окна редактирования и при вызове метода:

void SetModify(BOOL bModified=TRUE);

Устанавливает или сбрасывает флаг модификации (см.
предыдущий метод). Флаг сбрасывается при вызове метода с параметром FALSE и устанавливается при модификации содержимого окна редактирования или при вызове SetModify с параметром TRUE.



BOOL SetReadOnly(BOOL bReadOnly=TRUE);



Устанавливает режим просмотра (bReadOnly=TRUE) или редактирования (bReadOnly=FALSE).



TCHAR GetPasswordChar() const;



Возвращает символ, который при выводе пароля будет появляться на экране вместо символов, набираемых пользователем. Если такой символ не определен, возвращается 0. Устанавливается этот символ методом (по умолчанию используется "*"):



void SetPasswordChar(TCHAR ch);

void LimitText(int nChars=0);



Устанавливает максимальную длину в байтах текста, который может ввести пользователь. Если значение параметра равно 0, длина текста устанавливается равной UINT_MAX.



Методы работы с многострочным редактором

void LineScroll(int nLines, int nChars=0);



Прокручивает текст в области редактирования. Параметр nLimes задает число строк для вертикальной прокрутки. Окно редактирования не прокручивает текст дальше последней строки. При положительном значении параметра область редактирования сдвигается вдоль текста к последней строке, при отрицательной - к первой.

Параметр nChars задает число символов для горизонтальной прокрутки. Окно редактирования прокручивает текст вправо, даже если строки закончились. В этом случае в области редактирования появляются пробелы. При положительном значении параметра область редактирования сдвигается вдоль к концу строки, при отрицательном - к началу.



int GetFirstVisibleLine() const;



Возвращает номер первой видимой строки.



int GetLineCount() const;



Возвращает число строк текста, находящегося в буфере редактирования. Если текст не вводился, возвращает 1.



int GetLine(int nIndex, LPTSTR lpszBuffer) const;

int GetLine(int nIndex, LPTSTR lpszBuffer, int nMaxLength) const;



Копирует строку с номером, равным значению параметра nIndex, в буфер, заданный параметром lpszBuffer. Первое слово в буфере должно задавать его размер.При вызове второго варианта метода значение параметра nMaxLength копируется в первое слово буфера.

Метод возвращает число в действительности скопированных байтов. Если номер строки больше или равен числу строк в буфере окна редактирования, возвращает 0. Текст копируется без каких-либо изменений, нуль-символ не добавляется.



int LineIndex(int nLine=-1) const;



Возвращает номер первого символа в строке. Неотрицательное значение параметра принимается в качестве номера строки. Значение -1 задает текущую строку. Если номер строки больше или равен числу строк в буфере окна редактирования (строки нумеруются с 0), возвращается 0.


Некоторые методы класса CListBox


void ResetContent();

Очищает содержимое списка, делая его пустым.

int AddString( LPCSTR lpszItem);

Добавляет строку lpszItem в список и сортирует его, если при создании включено свойство Sort. В противном случае элемент добавляется в конец списка.

int DeleteString( UINT nIndex);

Удаляет из списка элемент с индексом nIndex. Индексация элементов начинается с 0.

int GetCurSel() const;

Получает индекс элемента, выбранного пользователем.

int SetCurSel( int nSelect);

Отмечает элемент с индексом nSelect как выбранный элемент списка. Если значение параметра равно -1, список не будет содержать отмеченных элементов.

int GetText( int nIndex, LPSTR lpszBuffer) const;

void GetText( int nIndex, CString& rString) const;

Копирует элемент с индексом nIndex в буфер.

int SetTopIndex( int nIndex);

Организует прокрутку списка в окне так, чтобы элемент с индексом nIndex был видимым.

int FindString( int nStartAfter, LPCSTR lpszItem) const;

Организует поиск в списке и возвращает в качестве результата индекс элемента списка, префикс которого совпадает со строкой lpszItem. Результат не зависит от регистра, в котором набирались символы сравниваемых строк. Параметр nStartAfter задает начало поиска, но поиск идет по всему списку. Он начинается от элемента, следующего за nStartAfter, до конца списка и затем продолжается от начала списка до элемента с индексом nStartAfter. В качестве результата выдается первый найденный элемент, удовлетворяющий условиям поиска. Если такого нет, результат получает значение LB_ERR.

int FindStringExact( int nIndexStart, LPCSTR lpszFind) const;

Этот метод отличается от предыдущего тем, что теперь не префикс элемента должен совпадать со строкой lpszFind, а сам элемент. Поиск по-прежнему не чувствителен к регистру, в котором набираются символы.



Некоторые сведения о программировании Windows-приложений


MFC – это базовый набор (библиотека) классов, написанных на языке С++ и предназначенных для упрощения и ускорения процесса программирования под Windows. Перед изучением библиотеки MFC и ее использованием для создания Windows-приложений, следует вспомнить, как работает сама Windows и каковы принципы взаимодействия программ с ней, какова структура типичной Windows-программы.



Немодальная диалоговая панель


Процедура создания и использования немодальной диалоговой панели несколько отличается от процедуры создания модальной диалоговой панели.

Панель и ее класс диалога

Как и в случае с модальной диалоговой панелью, в первую очередь необходимо создать шаблон диалоговой панели и добавить его в файл ресурсов приложения. Затем нужно создать класс CDlg, управляющий диалоговой панелью, - класс диалоговой панели. Этот класс наследуется непосредственно от базового класса CDialog. Затем можно модифицировать шаблон и класс панели для нужд приложения.

В классе немодального диалога следует переопределить метод Create базового класса CDialog следующим образом:

void CDlg::Create(CWnd* pParent) { CDialog::Create(IDD,pParent); // Вызов метода базового класса Parent=pParent; // окно-родитель }

Предварительно в класс диалога необходимо добавить элемент Parent - указатель на родительский объект, например, CWnd *Parent - указатель на родительское окно. Если родительский объект не окно, то можно добавить в метод Create параметр, отвечающий за передачу указателя на родительский объект, и присвоить его переменной Parent, объявив его в определении класса как указатель на объект соответствующего типа. Указатель на родительский объект позволяет при необходимости вызывать для родительского объекта методы его класса непосредственно из методов класса диалога, что обеспечивает связь немодального диалога и его родительского объекта.

В переопределенном методе Create следует обязательно вызвать метод Create базового класса CDialog. Именно этот метод создает окно диалоговой панели. В классе CDialog определены два прототипа метода Create. Один позволяет указать диалоговую панель через ее текстовое имя, а другой - через числовой идентификатор. Метод Create возвращает управление сразу после отображения на экране диалоговой панели. Он возвращает ненулевое значение, если создание диалоговой панели завершилось успешно, и нуль в противном случае.

При помощи ClassWizard в классе CDlg также следует создать заготовки методов-обработчиков сообщений от кнопок IDOK (OnOK) и IDCANCEL (OnCancel) и для сообщения WM_DESTROY (OnDestoy).
Затем необходимо изменить методы OnOK и OnCancel так, чтобы они вызывали только метод DestoyWindow. Если в методе Create предусматривается динамическое выделение блоков памяти, тогда в методе OnDestoy необходимо освободить занимаемую память.



Класс родительского объекта



Для обеспечения работы с немодальным диалогом сначала необходимо в классе приложения, в котором вызывается диалог (например, в классе окна приложения), объявить элемент Dlg класса CDlg. Родительский объект в методах своего класса может вызывать для диалоговой панели методы ее класса через объект Dlg, что обеспечивает связь родительского объекта и порождаемого им немодального диалога.



В момент создания объекта класса CDlg диалоговая панель как окно еще не создана и не появляется на экране. Для этого надо вызвать метод Create класса CDlg. Если диалоговая панель имеет стиль WS_VISIBLE, то она сразу появляется на экране. В противном случае для этого надо вызвать метод ShowWindow. Итак, в методе, который вызывает немодальный диалог (например, в методе-обработчике какого-либо сообщения для окна приложения), следует проверить, не отображается ли уже эта диалоговая панель, а затем создать ее методом Create класса CDlg:

if(Dlg.GetSafeHwnd()==NULL) Dlg.Create(this);

Чтобы закрыть немодальную диалоговую панель, можно воспользоваться методом DestroyWindow:

if(Dlg.GetSafeHwnd()!=NULL) Dlg.DestroyWindow();

Метод DestroyWindow определен в классе CWnd, следовательно, его можно вызывать для объектов класса диалоговой панели.


Несколько советов по работе с динамической памятью


Чем интенсивнее используется куча, тем в большей мере она фрагментируется и тем медленнее работает программа. Если предполагается, что время непрерывного функционирования программы будет исчисляться часами или днями, то следует проявить осторожность. Лучше выделить всю необходимую память при запуске программы и освободить ее при закрытии, но это не всегда возможно. Тут может помешать класс CString, который постоянно выделяет и освобождает крошечные порции памяти. Однако разработчики MFC недавно внесли в него ряд улучшений.

Если программа в основном выделяет блоки малого размера, но изредка требует 6ольших блоков (занимая их на время, отличное от того, на которое она занимает малые блоки), тогда стоит подумать о выделении крупных блоков функцией HeapAlloc, а не new. Это несколько уменьшит степень фрагментированности CRT-кучи. Не следует забывать время от времени вьзывать _heapmin. Стоит тщательно следить за тем, как был получен тот или иной блок памяти – приложению не миновать серьезных проблем, если оно, например, вызовет HeapFree для указателя, полученного с помощью new.

Следует учесть, что размер стека может быть таким, как надо. Так как теперь нет ограничения в 64 Кб, в стек можно помещать объекты большого размера, что уменьшает необходимость в распределении памяти из кучи.



Объединение создания и инициализации


После того, как объект создан клиентом, следующим шагом обычно является инициализация объекта путем выдачи ему команды загрузить перманентные данные. Например, клиент может вызвать метод IPersistFile::Load вновь созданного объекта для загрузки данных из файла. Если объект хранит свои перманентные данные, используя структурированное хранилище, клиент может вызвать IPersistStorage::Load, передав указатель на соответствующее хранилище.

Этот двухэтапный процесс создания и затем инициализации при использовании удаленного объекта не претерпевает никаких изменений — клиент по-прежнему выполняет оба шага и волен делать это традиционным способом, вызвав сначала СоCreateInstance или CoCreateInstanceEx и обратившись затем к соответствующему методу интерфейса IPersist для инициализации объекта. Но если объект выполняется на удаленной машине, то выполнение этих шагов требует серии циклов "запрос-ответ", что может быть неприемлемо медленно. Чтобы улучшить ситуацию, DCOM предоставляет клиентам две альтернативные функции, каждая из которых создает и инициализирует объект за один прием. Если объект выполняется локально, то использование этих функций представляет собой главным образом лишь дополнительное удобство (хотя некоторый выигрыш в производительности достигается и здесь за счет сокращений числа вызовов между процессами), но для удаленных объектов реализация этих функций оптимизирована.

Первая функция — CoGetInstanceFromFile — создает новый объект и инициализирует его данными из файла. Параметры функции включают машину, на которой создается объект, CLSID, имя файла и, подобно CoCreateInstanceEx, список IID нужных клиенту интерфейсов. Если CLSID не задан, функция пытается определить его по имени файла так, как это делает файловый моникер. Использование этой функции аналогично вызову CoCreateInstanceEx и последующему обращению к методу IPersistFile::Load объекта. Вторая функция — CoGetInstanceFromIStorage — работает сходным образом за исключением того, что ей передается указатель на IStorage (он задает соответствующее хранилище), а не имя файла. Вызов второй функции аналогичен вызову CoCreateInstanceEx с последующим обращением к методу IPersistStorage::Load объекта.


Хотя обе функции позволяют указать машину, на которой должен быть создан объект, ни для той, ни для другой эта информация не обязательна. Если имя машины не задано, место создания объекта зависит от нескольких факторов. Когда в локальном реестре данный CLSID связан с именем удаленной машины, как было описано выше, объект создается на указанном компьютере. А если запись для класса в реестре содержит значение ActivateAtStorage, то объект создается на той машине, где расположен заданный файл или хранилище.

Возможно также, что машина, на которой вызывается CoGetInstanceFromFile или CoGetInstanceFromIStorage, вообще не имеет информации о данном классе — локальный реестр может не содержать записи для соответствующего CLSID. Тогда предпринимается попытка создать объект на той машине, где находится указанный файл или хранилище. Если и в реестре той машины нет никакой информации о данном класса, вызов функции завершается с ошибкой. В противном случае объект создается на той же машине, где находится файл или хранилище, как если бы в локальном реестре было значение ActivateAtStorage.


Объектный RPC


После запуска удаленного объекта и получения указателей его интерфейсов клиент может вызывать методы этих интерфейсов. Вызов метода объекта "в процессе", по сути, означает непосредственное обращение к нему через виртуальную таблицу (а в случае диспинтерфейса и через IDispatch::Invoke). Обращение к методу объекта, реализованного в локальном сервере, требует участия заместителя, заглушки и некоторого механизма коммуникаций между процессами.

Вызов метода объекта, реализованного в удаленном сервере, также использует заместитель и заглушку, но в данном случае клиенту необходимо выполнить вызов удаленной процедуры (RPC) сервера. Протоколов RPC хватает в избытке, и Microsoft решила не создавать новый, но адаптировать существующий. Этот протокол — Microsoft называет его MS RPC — заимствован из OSF DCE (Microsoft, однако, не заимствовала у OSF реализацию протокола; MS RPC представляет собой другую реализацию DCE RPC, созданную на основе общедоступной спецификации). Как сам MS RPC, так и его модификация для DCOM — объектный RPC (Object RPC) — посылают по сети информацию в том же формате, что и DCE RPC (т.е. используют ту же структуру пакета). Хотя ORPC включает ряд новых соглашений по взаимодействию клиента с сервером, добавляет несколько новых типов данных и использует некоторые поля пакета особым образом, сама структура пакетов осталась прежней.

DCE RPC и MS RPC на самом деле включают в себя два разных протокола, которые поддерживаются и ORPC. Один из них — CN или СО — используется поверх протоколов с установлением логических соединений (connection-oriented protocols), таких как TCP (Transmission Control Protocol). Поскольку CN подразумевает, что нижележащий протокол гарантирует надежную доставку данных, то он не проверяет точность передачи. Другой протокол — DG или CL — используется поверх транспорта без установления логического соединения (также называемого протоколом дейтаграмм), такого как UDP (User Datagram Protocol). Рассматривая нижележащий протокол как совершенно ненадежный, DG реализует собственные механизмы, гарантирующие надежную доставку данных.
Для выдающего запрос клиента оба протокола выглядят совершенно одинаково, хотя нижележащие транспортные протоколы ведут себя абсолютно по-разному. Эти различия скрываются соответствующим протоколом RPC.



Независимо от используемого протокола клиент должен обладать информацией связывания (binding information) с пунктом назначения, прежде чем он выполнит вызов ORPC. В составе этой информации обычно сетевой адрес удаленной машины (например, адрес IP) и указание, какая комбинация протоколов должна использоваться (например, CL RPC и UDP). Информация связывания может включать точку назначения транспорта (transport endpoint) — ее часто называют портом. — задающую конкретный процесс на удаленной машине. Информацию связывания удобно представлять как строковое связывание (string binding) — символьной строкой, содержащей всю необходимую информацию.

Информацию связывания с данной удаленной системой клиент может получить по-разному. Например, имя машины, передаваемое функции CoCreateInstanceEx, может быть использовано для определения по крайней мере части информации связывания. Кроме того, возможна передача информации связывания (или ссылки на нее) от одного объекта другому. Чтобы понять, как это работает и почему это важно, сначала необходимо разобраться с ролью, которую играют OXID и разрешатели OXID.


Объекты GDI


При рисовании фигур в Visual C++ используются специальные объекты GDI, т.е. объекты интерфейса графического устройства (GDI - Graphics Device Interface) системы Windows.

Название

Класс

Тип определителя в Windows

Перо

CPen

HPEN

Кисть

CBrush

HBRUSH

Шрифт

CFont

HFONT

Растровое изображение

CBitmap

HBITMAP

Палитра

CPalette

HPALETTE

Область (регион)

CRgn

HRGN

Как и любые другие объекты в Visual C++, они должны быть созданы соответствующим образом. Создание объекта должно включать в себя и связывание с соответствующим объектом системы Windows. Эта операция осуществляется методом, имя которого начинается с префикса Create. Он может быть выполнен как после создания объекта Visual C++, так и в конструкторе объекта:

CPen Pen1; Pen1.CreatePen(PS_DOT, 5, RGB(0,0,0)); // вариант 1 CPen Pen2(PS_DOT, 5, RGB(0,0,0)); // вариант2

При завершении работы с объектом GDI необходимо обеспечить (вернее, не нарушать) его автоматическое удаление при выходе из соответствующей области видимости.

Перо - объект для рисования линий. При создании пера можно задать его ширину, цвет и тип линии. Для создания пера используются метод CreatePen.

Кисть представляет собой растровое изображение размером 8х8 пикселей для заполнения различных областей. Различают два варианта понятия кисти: логическая и физическая. Объект GDI задает логическую кисть, которая определяет полный набор свойств: цвет, стиль и т.п., - указываемых при ее создании. Возможно, не все эти свойства могут быть реализованы на данном компьютере. Свойства физической кисти реально воспроизводятся физическими устройствами. Они являются наиболее близким реализуемым приближением к свойствам логической кисти.

Различают четыре вида логических кистей, для создания которых используются различные методы:

Для создания сплошной кисти, все пиксели которой одного цвета, используется метод CreateSolidBrush.

Стандартные кисти уже имеются в операционной системе (таких кистей 7). Чтобы их создать, используется метод CreateStockBrush. Этот метод на самом деле создает только объект класса CBrush.
Сама кисть берется готовой из операционной системы.

Штриховые кисти имеют цвет и штриховой рисунок. Имеется 6 видов рисунка. Кисти создаются такими методами CreateHatchBrush и GreateSysColorBrush.

Шаблонные кисти могут иметь произвольный рисунок, задаваемый растровым изображением (BMP), либо аппаратно-независимым растровым изображением (DIB). Для создания кисти используются методы CreatePatternBrush, CreateDIBPatternBrush.

Растровое изображение представляет собой объект, инкапсулирующий прямоугольную область, состоящую из пикселов. Создав такой объект, можно затем задавать в этой области любое изображение, а также считывать и записывать ее в файл и производить с ней другие действия.



Палитры появились потому, что многие типы мониторов физически могут воспроизводить очень много цветов, а видеокартам не хватает видеопамяти, чтобы поддерживать все цвета одновременно. Например, монитор воспроизводит сотни тысяч цветов, а видеокарта отводит для одного пиксела байт и тем самым может хранить 256 цветов для окраски одной точки. Для более полного использования возможностей монитора и существуют палитры. Они сопоставляют цвета числам от 0 до 2n-1, которые могут храниться в ячейке, отведенной для одного пиксела.

В Windows различают понятия физической и логической палитры. Логическая предназначена для работы самого приложения. Программист не ограничен в выборе палитры. Два приложения могут использовать две различные палитры и осуществлять вывод на один и тот же экран. При этом физическая, естественно, может быть установлена только одна.

Когда нужного цвета логической палитры нет в физической, Windows пытается добавить его, а если это невозможно, коммутирует этот цвет логической палитры с наиболее близким цветом физической.

Для создания палитры используются методы CreatePalette и CreateHalftonePalette. Созданную палитру можно изменить методом SetPaletteEntries.



В Windows существует еще один способ управления цветом - макросом RGB. Он задает функцию, возвращающую значение типа COLORREF.У нее три параметра, задающий красный, зеленый и голубой компонент устанавливаемого цвета. Каждый компонент может принимать значения от 0 до 255. Итоговый цвет получается смешением красного, зеленого и синего цветов в соответствующих пропорциях.


Объекты графического интерфейса (класс CGdiObject)


Для отображения информации используются различные объекты графического интерфейса - GDI-объекты. Для каждого из этих объектов библиотека MFC содержит описывающий его класс, наследованный от базового класса CGdiObject.

Для работы с GDI-объектами используются классы:

CBitmap - растровое изображение bitmap.

CBbrush - кисть.

CFont - шрифт.

CPalette - палитра цветов.

CPen - -перо.

CRgn - область внутри окна.



Объекты класса CWnd и Windows-окна


Рассмотрим взаимоотношения объектов класса CWnd с Windows-окнами. Хотя объекты класса CWnd и производных от него классов (оконные объекты) не тождественны Windows-окнам, они связаны настолько тесно, что такое отождествление вполне оправдано. Windows-окно - объект операционной системы Windows - это внутренняя структура данных операционной системы, с которой связано изображение на экране в виде окна-прямоугольника со специальными элементами и областями. Эта структура обеспечивает управление окном на экране и реакцию на воздействие пользователя на окно. В смысле объектно-ориентированного программирования она во многом подобна классу и имеет метод - функцию окна. Однако эта структура не является, конечно же, структурой какого-либо языка программирования, поэтому Windows-окно не является объектом в смысле языка C++. Дескриптор (handle) Windows-окна просто идентифицирует его при вызове функций API, предназначенных для работы с Windows-окнами.

Оконный объект - это объект в смысле языка C++, который служит для работы с Windows-окном. Его переменная m_hWnd задает определитель (handle) Windows-окна, прикрепленного (attached) к данному оконному объекту. Создание оконного объекта состоит из двух этапов: создания самого объекта и создания и прикрепления Windows-окна к оконному объекту или прикрепление к оконному объекту уже существующего Windows-окна. Оконный объект может использоваться приложением для управления экранным образом (окном на экране) прикрепленного Windows-окна. При этом само Windows-окно становится "прозрачным", т.е. не фигурирует при вызове соответствующих методов.

Оконный объект использует большое количество функций API, предназначенных для работы с окнами. Например, метод MoveWindow передвигает и изменяет размер окна на экране:

wnd.MoveWindow(x, y, width, height, rep);

При вызове этого метода его реализация на самом деле использует API-функцию

BOOL MoveWindow( HWND hWnd, // дескриптор Windows-окна int X, // позиция по горизонтали int Y, // позиция по вертикали int nWidth, // ширина int nHeight, // высота BOOL bRepaint); // признак перерисовки


Функция API вызываетcя со значением первого параметра, равным дескриптору Windows-окна, прикрепленного к оконному объекту.



Таким образом, оконный объект как бы содержит в себе Windows-окно и предоставляет для работы с ним методы в смысле языка C++. О классе CWnd оконных объектов говорят, что он является классом-оболочкой для Windows-окон.



Ранее уже упоминалось об оконном объекте - облике (класс CView). Прикрепленное к облику Windows-окно служит для отображения документа на экране. Однако окно облика никогда не появляется на экране без окна другого объекта - окна-рамки. Окно-рамка (класс CFrameWnd) - объект производного от CWnd класса - координирует взаимодействие приложения с документом и его обликом. Прикрепленное к нему Windows-окно представлено в виде рамки, внутри которого находится окно облика. На экране они выглядят как одно целое, но на самом деле они прикреплены к разным объектам. Рамка имеет нестандартные элементы управления окна: полосу заголовка, меню, кнопки минимизации и максимизации, элементы управления размерами окна. Рамка также может иметь строку статуса и панель инструментов. Окно облика, наоборот, не имея элементов управления (они сосредоточены у рамки), является рабочей областью, в которой облик строит приложение.

Любое Windows-окно имеет две области: клиентскую область, изображение в которой задается приложением, и системную область, состоящую из управляющих элементов. Таким образом, окно-рамка задает Windows-окно, использующее только свою системную область, а облик задает Windows-окно, использующее только свою клиентскую область. Windows-окно, прикрепленное к облику, является дочерним (child) по отношению к Windows-окну, прикрепленному к соответствующему объекту окно-рамка. Это, в частности, означает, что дочернее окно нельзя передвинуть за пределы клиентской области родительского окна.

Число методов класса CWnd очень велико, с ними можно ознакомится при помощи справочной системы Visual C++. Остановимся подробнее только на некоторых из них.


Объекты синхронизации и классы MFC


Интерфейс Win32 поддерживает четыре типа объектов синхронизации - все они так или иначе основаны на понятии семафора.

Первым типом объектов является собственно семафор, или классический (стандартный) семафор. Он позволяет ограниченному числу процессов и потоков обращаться к одному ресурсу. При этом доступ к ресурсу либо полностью ограничен (один и только один поток или процесс может обратиться к ресурсу в определенный период времени), либо одновременный доступ получает лишь малое количество потоков и процессов. Семафоры реализуются с помощью счетчика, значение которого уменьшается, когда задаче выделяется семафор, то увеличивается, когда задача освобождает семафор.

Вторым типом объектов синхронизации является исключающий (mutex) семафор. Он предназначен для полного ограничения доступа к ресурсу, чтобы в любой момент времени к ресурсу мог обратиться только один процесс или поток. Фактически, это особая разновидность семафора.

Третьим типом объектов синхронизации является событие, или объект события (event object). Он используется для блокирования доступа к ресурсу до тех пор, пока какой-нибудь другой процесс или поток не заявит о том, что данный ресурс может быть использован. Таким образом, данный объект сигнализирует о выполнении требуемого события.

При помощи объекта синхронизации четвертого типа можно запрещать выполнения определенных участков кода программы несколькими потоками одновременно. Для этого данные участки должны быть объявлены как критический раздел (critical section). Когда в этот раздел входит один поток, другим потокам запрещается делать тоже самое до тех пор, пока первый поток не выйдет из данного раздела.

Критические разделы, в отличие от других типов объектов синхронизации, применяются только для синхронизации потоков внутри одного процесса. Другие же типы объектов могут быть использованы для синхронизации потоков внутри процесса или для синхронизации процессов.

В MFC механизм синхронизации, обеспечиваемый интерфейсом Win32, поддерживается с помощью следующих классов, порожденных от класса CSyncObject:


CCriticalSection - реализует критический раздел.

CEvent - реализует объект события

CMutex - реализует исключающий семафор.

CSemaphore - реализует классический семафор.

Кроме этих классов в MFC определены также два вспомогательных класса синхронизации: CSingleLock и CMultiLock. Они контролируют доступ к объекту синхронизации и содержат методы, используемы для предоставления и освобождения таких объектов. Класс CSingleLock управляет доступом к одному объекту синхронизации, а класс CMultiLock - к нескольким объектам. Далее будем рассматривать только класс CSingleLock.

Когда какой-либо объект синхронизации создан, доступ к нему можно контролировать с помощью класса CSingleLock. Для этого необходимо сначала создать объект типа CSingleLock с помощью конструктора:

CSingleLock( CSyncObject* pObject, BOOL bInitialLock = FALSE );

Через первый параметр передается указатель на объект синхронизации, например семафор. Значение второго параметра определяет, должен ли конструктор попытаться получить доступ к данному объекту. Если этот параметр не равен нулю, то доступ будет получен, в противном случае попыток получить доступ не будет. Если доступ получен, то поток, создавший объект класса CSingleLock, будет остановлен до освобождения соответствующего объекта синхронизации методом Unlock класса CSingleLock.

Когда объект типа CSingleLock создан, доступ к объекту, на который указывал параметр pObject, может контролироваться с помощью двух функций: Lock и Unlock класса CSingleLock.

Метод Lock предназначен для получения доступа к объекту к объекту синхронизации. Вызвавший его поток приостанавливается до завершения данного метода, то есть до тех пор, пока не будет получен доступ к ресурсу. Значение параметра определяет, как долго функция будет ожидать получения доступа к требуемому объекту. Каждый раз при успешном завершении метода значение счетчика, связанного с объектом синхронизации, уменьшается на единицу.

Метод Unlock освобождает объект синхронизации, давая возможность другим потокам использовать ресурс.


В первом варианте метода значение счетчика, связанного с данным объектом, увеличивается на единицу. Во втором варианте первый параметр определяет, на сколько это значение должно быть увеличено. Второй параметр указывает на переменную, в которую будет записано предыдущее значение счетчика.

При работе с классом CSingleLock общая процедура управления доступом к ресурсу такова:

создать объект типа CSyncObj (например, семафор), который будет использоваться для управления доступом к ресурсу;

с помощью созданного объекта синхронизации создать объект типа CSingleLock;

для получения доступа к ресурсу вызвать метод Lock;

выполнить обращение к ресурсу;

вызвать метод Unlock, чтобы освободить ресурс.

Далее описывается, как создавать и использовать семафоры и объекты событий. Разобравшись с этими понятиями, можно достаточно просто изучить и использовать два других типа объектов снхронизации: критические секции и мьютексы.


Обеспечение безопасного доступа к удаленному объекту


Объектный RPC предоставляет клиентам способ создания удаленных объектов и вызова их методов. Но возможность доступа к объектам на других системах повышает риск создания или использования таких объектов процессами или личностями, не имеющими соответствующих прав. Для минимизации подобного риска DCOM определяет стандартный способ доступа к сервисам защиты и их использования.

Здесь имеют место две проблемы. Первая заключается в контроле над тем, кто имеет право запускать серверы различных классов на данной удаленной машине — это сфера действия защиты активизации (activation security). Вторая проблема, состоящая в том, чтобы гарантировать контроль прав на вызовы клиентами методов уже исполняющихся объектов, известна как защита вызовов (call security). DCOM предоставляет решения обеих проблем.



OBJREF: передача указателей интерфейсов


Как и вызов локального метода, вызов удаленного метода может содержать параметры. Но разные машины иногда используют для представления одних и тех же данных разные форматы. Например, многие системы для представления символов применяют ASCII, тогда как мэйнфреймы IBM применяют для этого код EBCDIC. А разные компьютеры используют разные форматы представления целых и вещественных чисел. Для обеспечения взаимодействия машин, применяющих разные форматы данных, выполняется маршалинг параметров вызова ORPC с использованием сетевого формата NDR (Network Data Representation). NDR — стандартная часть DCE RPC (и, конечно же, MS RPC) — обеспечивает эффективный способ передачи параметров практически любых типов СОМ IDL между машинами, использующими разные представления этих типов данных. Необходимая трансляция параметров из NDR в локальное представление выполняется машиной, принимающей вызов.

Однако один тип параметров, часто передаваемый в вызовах ORPC, не имеет прямой поддержки NDR — это указатели на интерфейсы. Объекты часто передают указатели на интерфейсы своим клиентам, а клиент имеет право передать имеющийся у него указатель на интерфейс любому другому объекту. Когда указатель на интерфейс ссылается на объект в том же процессе, проблем не возникает: указатель передается, как есть. Когда указатель на интерфейс ссылается на объект в другом процессе на той же машине, передается ссылка на данный интерфейс внутри соответствующего процесса. Но когда указатель на интерфейс ссылается на объект, расположенный на другой машине, то, что должно быть передано, является весьма сложной конструкцией — объектной ссылкой (object reference — OBJREF).

Согласно протоколу DCOM в состав OBJREF входят:

OXID;

уникальный идентификатор данного интерфейса объекта — идентификатор указателя интерфейса (interface pointer identifier — IPID);

идентификатор объекта (object identifier — OID), определяющий объект;

строковое связывание для разрешателя OXID на той машине, где исполняется объект.

После передачи OBJREF от одного объекта другому принимающий объект может вызывать методы с помощью указателя на интерфейс, представляемого данной OBJREF.


Но как объект, получающий указатель на интерфейс, определяет, как связаться с объектом, заданным этим указателем? Точнее, откуда берется информация связывания с объектом, не являющаяся частью самой OBJREF? Есть два варианта. Если любой объект на локальной машине уже связывался недавно с объектом, имеющим тот же OXID, что и объект, на который ссылается указатель, то данный OXID уже есть в таблице локального разрешателя OXID. Если же информация связывания в таблице отсутствует, ее надо запросить у машины, на которой исполняется объект, заданный данной OBJREF.

Чтобы получить необходимую для использования новой OBJREF информацию связывания, DCOM на машине клиента, получившего OBJREF, обращается за помощью к локальному разрешателю OXID, передавая ему эту OBJREF. (Все это, конечно, скрыто от программиста и является частью инфраструктуры, обеспечиваемой DCOM.) Разрешатель OXID выделяет OXID из OBJREF и пытается отыскать его в собственной таблице OXID. Если объект с тем же OXID уже используется кем-то на этой машине, то в таблице будет запись для данного OXID с необходимым строковым связыванием. Предположим, объект А получил OBJREF интерфейса, поддерживаемого объектом X, расположенным на другой машине. Чтобы получить информацию связывания для осуществления вызовов с помощью нового указателя, реципиент передает OBJREF своему локальному разрешателю OXID. Разрешатель OXID может сразу возвратить нужную информацию, если она уже есть в его таблице.

Однако допустим, что OBJREF ссылается на интерфейс удаленного объекта Q, для которого у локального разрешателя OXID информации нет. Тогда разрешатель OXID извлекает из OBJREF информацию связывания с разрешателем OXID на машине, где исполняется объект Q, и с помощью ее связывается с этим разрешателем
. Для этого локальный разрешатель OXID вызывает метод ResolveOxid интерфейса lObjectExporter разрешателя OXID на удаленной машине. В качестве параметра этого вызова передается OXID, выделенный из только что полученной OBJREF. Данный вызов возвращает строковое связывание для данного OXID, которое затем добавляется локальным разрешателем в свою таблицу.


Вновь полученный указатель на интерфейс теперь можно использовать.

Здесь возникает естественный вопрос: почему в составе OBJREF передается не строковое связывание самого объекта, но связывание разрешателя OXID на машине объекта? Ведь тогда клиентскому разрешателю OXID вообще не пришлось бы связываться с разрешателем OXID объекта — полученная OBJREF уже содержала бы все необходимое для связи с объектом.

Чтобы понять, почему архитекторы DCOM приняли иное решение, вспомним, что DCOM позволяет клиенту работать с удаленным объектом с помощью разных протоколов: TCP/IP, UDP/IP, IPX/SPX и др. Для каждого поддерживаемого объектом протокола может потребоваться загрузка отдельной DLL, которой обычно необходимы дополнительные потоки для ожидания поступления запросов по данному протоколу. Так что объекту, работающему с несколькими протоколами, это может дорого обойтись. Не удивительно, что архитекторы DCOM стремились снизить накладные расходы, загружая протоколы только тогда, когда это необходимо, и пытаясь обойтись их минимальным количеством.

С этой целью объекты используют отложенную регистрацию протоколов (lazy protocol registration). Другими словами, объект загружает необходимый для протокола код, лишь когда клиент захочет работать с ним по данному протоколу.
Например, когда разрешатель OXID объекта получает от клиентской машины вызов IObjectExporter::ResolveOxid, этот запрос выполняется с помощью протокола, скажем, TCP/IP. Разрешатель OXID на машине объекта может определить, загружен ли уже объектом код TCP/IP. Если нет, разрешатель приказывает объекту загрузить соответствующий код, после чего клиент и объект могут взаимодействовать по TCP/IP. Если затем другой разрешатель OXID запросит связь с тем же объектом по UDP/IP, объект получит указание загрузить код и этого протокола. В то время как разрешатели OXID обязаны ожидать вызовы по всем протоколам, поддерживаемым данной машиной, конкретный объект загружает код только протоколов, явно запрошенных его клиентами. Данный подход иногда требует дополнительного обмена данными при установлении соединения, но он позволяет объектам избежать напрасной траты ресурсов на не используемые ими протоколы.




Обработка окном сообщений


Работа операционной системы Windows основана на обработке сообщений. Когда пользователь работает с устройствами ввода/вывода (например, клавиатурой или мышью), драйверы этих устройств создают сообщения, описывающие его действия.

Сообщения сначала попадают в системную очередь сообщений операционной системы. Из нее сообщения передаются приложениям, которым они предназначены, и записываются в очередь приложений. Каждое приложение имеет собственную очередь сообщений.

Приложение в цикле, который называется циклом обработки сообщений, получает сообщения из очереди приложения и направляет их соответствующей функции окна, которая и выполняет обработку сообщения. Цикл обработки сообщений в традиционной Windows-программе обычно состоял из оператора while, в котором циклически вызывались функции GetMessage и DispatchMessage. Для более сложных приложений цикл обработки сообщений содержал вызовы других функций (TranslateMessage, TranslateAccelerator). Они обеспечивали предварительную обработку сообщений.

Каждое окно приложения имеет собственную функцию окна. В процессе обработки сообщения операционная система вызывает функцию окна и передает ей структуру, описывающую очередное сообщение. Функция обработки сообщения проверяет, какое именно сообщение поступило для обработки, и выполняет соответствующие действия.

Если при создании приложения используется библиотека классов MFC, то за обработку сообщений отвечают классы. Любой класс, наследованный от базового класса CCmdTarget, может обрабатывать сообщения. Чтобы класс смог обрабатывать сообщения, необходимо, чтобы он имел таблицу сообщений класса. В этой таблице для каждого сообщения указан метод класса, предназначенный для его обработки.



Обычные MFC DLL


Обычные MFC DLL позволяют применять MFC в динамических библиотеках. При этом приложения, обращающиеся к таким библиотекам, не обязательно должны быть построены на основе MFC. В обычных DLL можно использовать MFC любым способом, в том числе создавая в DLL новые классы на базе классов MFC и экспортируя их в приложения.

Однако обычные DLL не могут обмениваться с приложениями указателями на классы, производные от MFC.

Если приложению необходимо обмениваться с DLL указателями на объекты классов MFC или их производных, нужно использовать расширение DLL, описанное в следующем разделе.

Архитектура обычных DLL рассчитана на использование другими средами программирования, такими как Visual Basic и PowerBuilder.

При создании обычной библиотеки MFC DLL с помощью AppWizard выбирается новый проект типа MFC AppWizard (dll). В первом диалоговом окне мастера приложений необходимо выбрать один из режимов для обычных динамических библиотек: “Regular DLL with MFC statistically linked” или “Regular DLL using shared MFC DLL”. Первый предусматривает статическое, а второй — динамическое подключение библиотек MFC. Впоследствии режим подключения MFC к DLL можно будет изменить с помощью комбинированного списка на вкладке “General” диалогового окна “Project settings”.

Управление информацией о состоянии MFC

В каждом модуле процесса MFC содержится информация о его состоянии. Таким образом, информация о состоянии DLL отлична от информации о состоянии вызвавшего ее приложения. Поэтому любые экспортируемые из библиотеки функции, обращение к которым исходит непосредственно из приложений, должны сообщать MFC, какую информацию состояния использовать. В обычной MFC DLL, использующей динамические библиотеки MFC, перед вызовом любой подпрограммы MFC в начале экспортируемой функции нужно поместить следующую строку:

AFX_MANAGE_STATE(AfxGetStaticModuleState()) ;

Данный оператор определяет использование соответствующей информации о состоянии во время выполнения функции, обратившейся к данной подпрограмме.



Обзор среды Microsoft Developer Studio


Студия разработчика фирмы Microsoft (Microsoft Developer Studio) - это интегрированная среда для разработки, позволяющая функционировать различным средам разработки, одна из которых Visual C++, другая - Visual J++. В дальнейшем будет идти речь только о среде разработки Visual C++.

В студии разработчика можно строить обычные программы на C и С++, создавать статические и динамические библиотеки, но основным режимом работы является создание Windows-приложений с помощью инструмента MFC AppWizard (Application Wizard - мастер приложений) и библиотеки базовых классов MFC (Microsoft Foundation Class Library). Такие приложения называются MFC-приложениями. Главная особенность этих Windows-приложений состоит в том, что они работают как совокупность взаимодействующих объектов, классы которых определены библиотекой MFC.



Обзор технологий ActiveX и OLE


Как OLE, которая теперь снова обозначает только технологии создания составных документов, так и широкий набор технологий под маркой ActiveX, разработаны с использованием СОМ. Многие из этих технологий своими корнями уходят в поддержку составных документов, тогда как другие предназначены совершенно для других целей. Данный раздел содержит краткое введение в наиболее важные технологии СОМ.