Для создания мини-сервера на базе MFC особых познаний в OLE не требуется. Этот пример покажет взаимодействие компонентов и контейнеров. В данном случае мы создадим не сложный компонент. Он просто выводит в окно некоторый текст и графику. Текст хранится в документе, и для его редактирования предусмотрено диалоговое окно. Приступаем к созданию приложения компонента (мини-сервера)
1. Создание каркаса приложения мини-сервер
Запускаем Visual Studio 2010, заходим в меню «Файл» -> «Создать» -> «Проект…», в появившемся окошке «Создать проект» выбираем «Приложение MFC» и вводим имя проекта «MyMini-server»
нажимаем «Ок», в появившемся окошке «Мастер приложений MFC» жмем «Далее» в появившемся окошке выставляем
жмем кнопку «Далее», в появившемся окошке выставляем «Мини сервер»
и жмем кнопку «Готово». У нас мастер сгенерирует приложение со следующими файлами
у нас создались файлы класса вида и класса документа наследника не от CDocument, а наследуется уже от COleServerDocEx и создалось два новых класса class CInPlaceFrame : public COleIPFrameWndEx это вороде отрисовывает окно в контейнере и class CMyMiniserverSrvrItem : public COleServerItem — а этот вроде как класс вида выступает в контейнере.
2. Создание самого приложения
Добавим в класс документа открытую переменную типа CString m_strText, а в функции члене документа CMyMiniserverDoc::OnNewDocumen задайте начальное значение этой переменной «Initial default text».
1 2 3 4 5 6 7 8 9 10 11 |
BOOL CMyMiniserverDoc::OnNewDocument() { if (!COleServerDocEx::OnNewDocument()) return FALSE; // TODO: добавьте код повторной инициализации // (Документы SDI будут повторно использовать этот документ) m_strText="initial default text"; return TRUE; } |
Создадим новое диалоговое окошко, вот его вид
добавим к нему класс CTextDialog, производный от CDialog. Не забудьте включить файл диалогового окна в файл MyMini-serverDoc.cpp. И добавим к диалогу переменную для поля ввода CString m_strText (добавим ее в CTextDialog), вот код файлов нашего сгенерированого диалога и с добавленной переменной
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#pragma once // диалоговое окно CTextDialog class CTextDialog : public CDialog { DECLARE_DYNAMIC(CTextDialog) public: CTextDialog(CWnd* pParent = NULL); // стандартный конструктор virtual ~CTextDialog(); // Данные диалогового окна enum { IDD = IDD_DIALOG1 }; protected: virtual void DoDataExchange(CDataExchange* pDX); // поддержка DDX/DDV DECLARE_MESSAGE_MAP() public: CString m_strText; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// TextDialog.cpp: файл реализации // #include "stdafx.h" #include "MyMini-server.h" #include "TextDialog.h" #include "afxdialogex.h" // диалоговое окно CTextDialog IMPLEMENT_DYNAMIC(CTextDialog, CDialog) CTextDialog::CTextDialog(CWnd* pParent /*=NULL*/) : CDialog(CTextDialog::IDD, pParent) , m_strText(_T("")) { } CTextDialog::~CTextDialog() { } void CTextDialog::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT1, m_strText); } BEGIN_MESSAGE_MAP(CTextDialog, CDialog) END_MESSAGE_MAP() // обработчики сообщений CTextDialog |
Добавим в меню для внедряемого компонента IDR_SRVR_EMBEDDED новый пункт меню под названием «IDR_SRVR_EMBEDDED» и к нему добавьте подпункт «Modify», для меню активизируемого по месту IDR_SRVR_INPLACE новый пункт меню под названием «IDR_SRVR_INPLACE» и к нему добавьте подпункт «Modify». Сделайте одинаковые идентификаторы обоих пунктов меню что в одном меню что в другом, присвойте ему название ID_MODIFY. Дальше используя «мастер классов» создайте обработчик для пунктов меню onModify в классе документа. Код обработчика onModify выглядит так
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void CMyMiniserverDoc::OnModify() { // TODO: добавьте свой код обработчика команд CTextDialog dlg; dlg.m_strText=m_strText; if(dlg.DoModal()==IDOK) { m_strText=dlg.m_strText; UpdateAllViews(NULL);//инициирует вызов CMyMiniserverView::OnDraw UpdateAllItems(NULL);//инициирует вызов CMyMiniserverSrvItem::OnDraw SetModifiedFlag();//эту функцию нужно вызывать после любого изменения документа } } |
за функцию SetModifiedFlag почитать можно тут.
Переопределим функцию OnPrepareDC
1 2 3 4 5 6 7 |
void CMyMiniserverView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) { // TODO: добавьте специализированный код или вызов базового класса pDC->SetMapMode(MM_HIMETRIC); CView::OnPrepareDC(pDC, pInfo); } |
за функцию OnPrepareDC почитать можно тут.
Отредактируем функцию OnDraw в классе «вид». Следующий код выводит в центр клиентского прямоугольника окружность диаметром 2 см и текст (с переносом строк чтобы он умещался в окне)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
void CMyMiniserverView::OnDraw(CDC* pDC) { CMyMiniserverDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: добавьте здесь код отрисовки для собственных данных CFont font; font.CreateFontW(-500,0,0,0,400,FALSE,FALSE,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH|FF_SWISS,L"Arial"); CFont* pFont=pDC->SelectObject(&font); CRect rectClient; GetClientRect(rectClient); CSize sizeClient=rectClient.Size(); pDC->DPtoHIMETRIC(&sizeClient); CRect rectEllipse(sizeClient.cx/2-1000,-sizeClient.cy/2+1000, sizeClient.cx/2+1000, -sizeClient.cy/2-1000); pDC->Ellipse(rectEllipse); pDC->TextOutW(0,0,pDoc->m_strText); pDC->SelectObject(pFont); } |
Дальше отредактируем функцию OnDraw серверного элемента. Следующий код пытается изобразить тоже что и функция OnDraw класса «вид»
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
BOOL CMyMiniserverSrvrItem::OnDraw(CDC* pDC, CSize& rSize) { if (!pDC) return FALSE; // Удалите этот код, если вы используете rSize UNREFERENCED_PARAMETER(rSize); CMyMiniserverDoc* pDoc=GetDocument();//получаем документ // TODO: задайте режим отображения и величину // (Величина обычно совпадает с размером, возвращенным из OnGetExtent) pDC->SetMapMode(MM_ANISOTROPIC); pDC->SetWindowOrg(0,0); pDC->SetWindowExt(3000, -3000); // TODO: добавьте код отрисовки. Кроме того, заполните величину HIMETRIC. // Все рисунки производятся в контексте устройства метафайла (pDC). CFont font; font.CreateFont(-500, 0, 0, 0, 400, FALSE, FALSE, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Arial"); CFont* pFont = pDC->SelectObject(&font); CRect rectEllipse(CRect(500, -500, 2500, -2500)); pDC->Ellipse(rectEllipse); pDC->TextOut(0, 0, pDoc->m_strText); pDC->SelectObject(pFont); return TRUE; } |
Отредактируем функцию Serialize класса документа. О загрузке и сохранении текста документа в потоке данных Contents в основном хранилище объекта заботится каркас приложений. Все что требуется от нас это написать обычный код сериализации.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void CMyMiniserverDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO: добавьте код сохранения ar << m_strText; } else { // TODO: добавьте код загрузки ar >> m_strText; } } |
Соберем и зарегистрируем приложение. Для обновление реестра нужно всего один раз запустить в автономном режиме приложение.
3. Тестируем приложение
Для того чтобы протестировать наше приложение нам понадобится программа контейнер, мы будем использовать программу excel. У меня excel 2010, запускаем excel, выбираем «Вставка» и кликаем по кнопке «вставить объект»
в появившемся окошке «Вставить элемент» выбираем наш элемент
нажимаем «ок» и у нас добавился наш элемент
когда мы кликаем по элементу у нас запускается это от окошко. Когда мы кликаем правой клавишей по элементу у нас появляется контекстное меню
Если мы вибераем подпункт меню Edit то у нас вроде должно загружатбся меню IDR_SRVR_INPLACE, а если выбираем Open то IDR_SRVR_EMBEDDED, но у меня загружается все время меню IDR_MAINFRAME, в общем не знаю почему так происходит если загружается IDR_SRVR_EMBEDDED, то у нас происходит внедрение, а если IDR_SRVR_INPLACE то происходит активизация по месту. Не знаю почему у меня не работает, в книге написано что должно так работать как я описал. Но мы не будем теряться добавим к меню IDR_MAINFRAME новый пункт меню под названием «IDR_MAINFRAME» и подпункт меню Modify с идентификатором ID_MODIFY. Перекомпелируем программу, кликаем два раза мышкой по нашему компоненту и у нас уже появляется новый подпункт меню
Дальше изменяем текст на «Hellow world» для этого выберем подпункт меню Modify и запустим диалоговое окно и у нас в excele меняется текст. В общем все работает.
4. Отладка
Отлаживается этот компонент как и любое другое приложение COM. Заходим в свойства проекта, указываем путь к нашему файлу и прописываем команду /Embedding, запускаем отладку и она переходит в режим ожидания. Дальше заходим в эксель и внедряем наш компонент кликаем по нему запуская программу редактор наш сервер.
Либо можно в оладчике прописать путь к excel файлу с таким же параметром /Embedding и запустить отадку, она перейдет в режим ожидания, мы запускаем excel и все что будет компонент будет выводится в отладчике. (этот способ я не уверен что будет работать, по идее должен).
5. Немножко теории
Мини-сервер — это такой компонент который сам по себе не может запускаться, а только из программки контейнера. Бывают еще полные-сервера, те могут и сами по себе запускаться. Наше приложение пример мини-сервера, она сама по себе не может работать.
Visual Editing (визуальное редактирование) — это торговая марка Microsoft, синоним активизации по месту. Активизация по месту — это когда мы внедрили компонент и кликаем по нему мушкой у нас меняется меню и панели инструментов на те которые мы можем редактировать наше приложение. А просто внедрение запускает программку которая редактирует данные элемент.
Метафайл — это общий термин для формата файлов, которые могут дополнительно хранить в себе и данные (доп. сведения) о хранимых в них (файлах) данных — сведения, которые в обычном режиме просмотра содержимого сокрыты от пользователя. В мире OLE-внедрения метафайлы создаются компонентами, а воспроизводятся контейнерами.
[youtube]https://www.youtube.com/watch?v=xlefiSn9Kxc[/youtube]