Главная страница
Навигация по странице:

  • DialogBox

  • OnBnClickedButton1

  • ON_WM_ERASEBKGND() Изменение цвета окна

  • IDB_BITMAP1 После этого картинка окажется внутри контрола. Добавка изображения в Picture Control

  • Еще один способ динамической компоновки

  • LoadLibrary

  • Домашнее задание на неделю

  • Лекция 1 ООП. Вспоминаем Создание windows приложений c в visual studio Виды windows приложений c


    Скачать 2.22 Mb.
    НазваниеВспоминаем Создание windows приложений c в visual studio Виды windows приложений c
    АнкорЛекция 1 ООП
    Дата18.04.2022
    Размер2.22 Mb.
    Формат файлаppt
    Имя файлаoop22v4 (1).ppt
    ТипЛекция
    #482272

    ООП 2021-22 Весна. Лекция 4 Windows-программирование Диалоговые окна. Создание и применение DLL. oopCpp@yandex.ru

    (вспоминаем) Создание windows приложений c++ в visual studio

    Виды windows приложений c++ :


    1. Приложение Win32 — это классическое приложение Windows на языке C++, которое может использовать встроенные API -Интерфейсы C Windows и (или ) API CRT и API стандартной библиотеки, а также сторонние библиотеки.
    Приложение Win32, выполняемое в окне, требует, чтобы разработчик работал явно с сообщениями Windows внутри оконной процедуры Windows. Несмотря на имя, приложение Win32 можно скомпилировать как 32-разрядный (x86) или 64-разрядный (x64) двоичный файл. В интегрированной среде разработки Visual Studio термины x86 и Win32 являются синонимами.
    2. Модель COM — это спецификация, которая позволяет программам, написанным на разных языках, взаимодействовать друг с другом. Многие компоненты Windows реализуются как COM-объекты и следуют стандартным правилам COM для создания объектов, обнаружения интерфейса и уничтожения объектов. Использование объектов COM из классических приложений C++ относительно просто, но написание собственного COM-объекта является более сложным. Библиотека активных шаблонов (ATL) предоставляет макросы и вспомогательные функции, упрощающие разработку COM .


    3. Приложение MFC — это классическое приложение Windows, которое использует Microsoft Foundation Classes для создания пользовательского интерфейса. Приложение MFC также может использовать компоненты COM, а также API CRT и библиотеки стандартных библиотек. MFC предоставляет объектно-ориентированную оболочку с тонким C++ для циклов оконных сообщений и API Windows. MFC предоставляет удобные вспомогательные классы для управления окнами, сериализации, обработки текста, печати и современных элементов пользовательского интерфейса. Для эффективной работы с MFC вы должны быть знакомы с Win32.
    4. Приложение или компонент C++/CLI использует расширения для синтаксиса C++ (как это разрешено стандартом C++), чтобы обеспечить взаимодействие между .NET и машинным кодом C++.

    Классическое приложение Win32


    Классическое приложение на C++ — это приложение, которое имеет доступ к полному набору интерфейсов API Windows и запускается в окне или в системной консоли.
    Win32 API (далее WinAPI) – это набор функций (API – Application Programming Interface), работающих под управлением ОС Windows. Их объявления содержатся в заголовочном файле windows.h.
    С помощью WinAPI можно создавать различные оконные процедуры, диалоговые окна, программы и даже игры. Этот интерфейс лежит в основе Windows Forms, MFC, других библиотек.


    #include // заголовочный файл, содержащий функции API
    // Основная функция - аналог int main() в консольном приложении:
    int WINAPI WinMain(HINSTANCE hInstance, // дескриптор экземпляра приложения
    HINSTANCE hPrevInstance, // в Win32 не используется
    LPSTR lpCmdLine, // нужен для запуска окна в режиме командной строки
    int nCmdShow) // режим отображения окна
    {
    // Функция вывода окна с кнопкой "ОК" на экран
    MessageBox( NULL, L"Привет, мир!!!", L"Оконная процедура", MB_OK);
    return NULL; // возвращаем значение функции
    }


    В начале мы подключаем заголовочный файл windows.h. В нём содержатся все необходимые API- функции.
    WinMain – название функции. Она имеет четыре параметра. Первый из них – HINSTANCE hInstance. hInstance является дескриптором экземпляра окна (это некий код программы, идентификатор, по которой ОС будет отличать её от прочих программ).
    Переменная типа LPSTR с именем lpCmdLine используется в том случае, если мы запускаем окно через командную строку с явным указанием дополнительных параметров.
    Параметр nCmdShow - целочисленный, определяет способ показа окна. Нужен для функции ShowWindow. Например, с помощью него мы можем развернуть окно на весь экран, сделать его определённой высоты, прозрачным или поверх остальных.

    Диалоговые приложения

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


    // Дальше создадим файл main.cpp со следующим кодом:
    #include
    #include "resource.h"
    // процедура для обработки сообщений окна диалога
    BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE
    hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    int ret = DialogBox (hInstance, MAKEINTRESOURCE(IDD_DIALOG1),
    NULL, (DLGPROC) DlgProc );
    if (ret == 0) {
    MessageBox(NULL, (LPCTSTR)"Отмена", "Inform",
    MB_OK | MB_ICONINFORMATION);
    }
    else if (ret == 1) {
    //Если была нажата кнопка Ок
    MessageBox(NULL, (LPCTSTR)"Ок", "Inform",
    MB_OK | MB_ICONINFORMATION);
    }
    return ret;
    }


    BOOL CALLBACK DlgProc (HWND hwnd, UINT msg, WPARAM wParam,
    LPARAM lParam) { // оконная процедура
    switch (msg) {
    case WM_INITDIALOG: {
    return FALSE;
    }
    case WM_COMMAND: {
    switch (LOWORD(wParam)) {
    case IDOK:
    EndDialog(hwnd, 1);
    return TRUE;
    case IDCANCEL:
    EndDialog(hwnd, 0);
    return TRUE;
    }
    }
    case WM_CLOSE: {
    EndDialog(hwnd, 0);
    return FALSE;
    }
    }
    return FALSE;
    }

    DialogBox


    Здесь вызывается функция DialogBox() которой передается в качестве параметра собственная оконная процедура DlgProc() в которой мы обрабатываем 3 вида событий: WM_INITDIALOG, WM_COMMAND,  WM_CLOSE.
    При получении события WM_INITDIALOG необходимо инициализировать главное окно приложения.
    В обработку сообщения WM_CLOSE  попадаем когда окно закрывается.
    Сообщение WM_COMMAND служит для обработки собственных событий, в том числе отклик на выбор команды меню. Как видно наша процедура возвращает либо FALSE либо TRUE,  если мы возвращаем FALSE, то вызывается стандартная процедура, которая рисует окно, если мы вернем TRUE, то это означает что функция стандартная для прорисовки окна не вызовется. 


    Но в пустом проекте отсутствует файл ресурсов, где должны быть обозначены идентификаторы каждого из них: каждое окно имеет собственный HANDLE и ID-идентификатор.
    Поэтому создаем само диалоговое окно, как ресурс. Одновременно появится в проекте и файл ресурсов: "resource.h"


    Выбираем Dialog и нажимаем создать


    Диалог создан, в окне VS «Панель элементов» мы видим какие элементы можно добавить, а в окне «Свойства» изменить свойства каждого элемента. Дальше запускаем построение проекта, а затем его выполнение: создается окно с кнопкой Ок и Отмена.


    Может потребоваться переключение кодировки в свойствах проекта:


    Кроме того, в компоновщике должна быть настроена подсистема Windows, а не консоль

    Диалог с использованием MFC


    Выбираем в создании проекта приложение MFC (если в Студии нет – надо докачать)

    Есть два варианта. 


    Есть два варианта. 
    Первый вариант - “использовать MFC в общей библиотеке DLL" предполагает, что библиотека MFC похожа на распределенную динамическую связанную библиотеку (shared  DLL). Использование DLL в качестве динамической библиотеки ссылок уменьшает размер приложения, но усложняет установку продукта.
    Второй вариант - “использовать MFC в статической библиотеке" предлагает MFC в качестве статической библиотеки. В этом случае у вас есть файл “.exe” большого размера, но при этом не возникнет сложностей в обслуживании приложений. В этом случае модули библиотеки подключаются к файлу “.exe” статически, поэтому приложение легко перемещается с одного компьютера на другой.


    Проект создан. Можно посмотреть, что и как мастер организовал:


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


    Открываем файл ресурсов и начинаем редактировать содержимое диалогового окна:


    Находим панель элементов и выбираем несколько контролов для проверки: Edit, кнопку и картинку.


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


    У каждого ресурса (в настоящем случае – окна диалога) есть идентификатор, его значение задается макросом препроцессора, а располагается в файле Resource.h.


    При создании класса диалога в проект автоматически добавляются два новых файла с описанием класса (хидер) и его реализацией (cpp). В хидере можно увидеть:
    В cpp-файле автоматически создан код для управления содержимым окна диалога.


    Важнейшей функцией в классе диалога является DoDataExchange.
    Функция DoDataExchange() вызывается автоматически для обмена данными (и для проверки корректности данных), передаваемых между объектами классов диалогового окна и включенных в него объектов классов элементов управления (переключателей, полей редактирования, списков и т. п.):
    virtual void CWnd::DoDataExchange(
    CDataExchange* pDX); // Указатель на изменяемый объект
    Добавить любой элемент управления можно, "захватив" его в окне Toolbox и перетащив в окно диалога. Осуществить обработку сообщения по нажатию кнопки можно дважды щелкнув левой кнопкой мыши на кнопке в окне ресурса диалогового окна.


    Для управления элементами оконного приложения в Windows есть Хэндлы (Handle) и идентификаторы (ID).
    Хэндл – рукоятка, рычаг, переключатель – основное средство управления окнами и графическим элементами. Однако у окна может быть также идентификатор. В частности контролам присваивается число, максимальное значение которого : 0xFFFF. В приложениях MFC – эти значения обычно располагаются в файле Resource.h. В отличии от хэндла – их можно изменить, открыв окно свойств контрола. Вот как они используются:
    void CMFCDlgDlg::OnBnClickedButton1() {
    CWnd* idc = GetDlgItem(IDC_EDIT1); // найти окно по идентификатору
    idc->SetWindowText(L"PushButton");
    idc->UpdateWindow();
    CEdit* e = (CEdit *)GetDlgItem(IDC_EDIT1 ); // еще раз – можно так не делать
    CString s;
    e->GetWindowText( s);
    SetWindowText(s+s); // напечатать подряд два раза строку из Edit1
    }


    Хэндлы и идентификаторы


    В хидере, при реализации нового поведения (клика по кнопке) появится объявление функции
    afx_msg void OnBnClickedButton1();
    afx_msg - это указатель на то, что данная функция - это обработчик сообщения. Эта функция должна быть включена в карту обработчиков событий. Например так:
    BEGIN_MESSAGE_MAP (CMFCDlgDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_EN_CHANGE(IDC_EDIT1, &CMFCDlgDlg::OnEnChangeEdit1)
    ON_BN_CLICKED(IDC_BUTTON1, &CMFCDlgDlg::OnBnClickedButton1)
    END_MESSAGE_MAP()
    Если ее из карты убрать, то сообщение обрабатываться не станет.


    Чтобы окно диалога сделать нужным цветом необходимо задать обработку сообщения WM_ERASEBKGND
    Если делать вручную, то для этого надо:
    1. Объявить функцию обработки этого сообщения в хидере диалога
    afx_msg BOOL CMFCDlgDlg::OnEraseBkgnd(CDC* pDC);
    2. В карте обработки сообщений указать на необходимость отклика функции окна диалога:
    ON_WM_ERASEBKGND()


    Изменение цвета окна


    3. Собственно реализовать функцию:
    BOOL CMFCDlgDlg::OnEraseBkgnd(CDC* pDC) {
    CPen myPen[64];
    int i;
    CRect rect;
    for (i = 0; i <= 60; i++)
    myPen[i].CreatePen(PS_SOLID, 1, RGB((i * 4), 0, 0));
    CPen* oldPen = pDC->SelectObject(&myPen[0]);
    GetClientRect(&rect);
    for (i = 0; i <= rect.bottom; ) {
    pDC->MoveTo(0, i);
    pDC->LineTo(rect.right, i);
    i++;
    pDC->SelectObject(&myPen[i * 64 / rect.bottom]);
    }
    pDC->SelectObject(oldPen);
    return TRUE;
    }


    Для отображения картинки в Picture Control нужно:
    Добавить ресурс (выбрав левой клавишей мышки) – картинку BMP, после ее загрузки ей будет присвоен идентификатор IDB_BITMAP1
    Связать этот ресурс в контролом – выбрать в свойствах Picture Control пункт изображения, присваивая ему значение IDB_BITMAP1
    После этого картинка окажется внутри контрола.


    Добавка изображения в Picture Control

    С++ & dll & lib статическая и динамическая линковка библиотек


    (По материалам http://words-are-dust.blogspot.com/2013/10/dll-lib.html)
    Этапы сборки проекта
    Программа на C++ состоит из объявлений (типов, функций) и определений (реализаций тел функций, методов, выделение памяти под глобальные и статические объекты и т.п.).
    Объявления, как правило, выносятся в заголовочные файлы (т.н. "хидеры") с расширением (*.h), а определения оказываются в файлах исходного кода с расширением (*.cpp).
    Заголовочные файлы играют вспомогательную функцию и в команды не компилируются сами непосредственно (их содержимое копируются в файлы исходного кода с помощью команды #include "header_file_name.h").


    Файлы исходного кода содержат алгоритмы и компилируются непосредственно в команды процессора в виде объектных файлов (*.obj), по одному на каждый файл исходного кода.
    Чтобы собрать объектные файлы в один двоичный файл (непосредственно исполняемый *.exe, или библиотеки *.dll, *.lib) необходимо произвести линковку. Этим занимается программа Linker (компоновщик, линкер), ориентируясь по хидерам.
    Программирование ценно модульностью. То есть написав однажды код, мы можем его использовать во многих других проектах. Для этого часто бывают нужны бинарные (двоичные) библиотеки, которые уже скомпилированы, но можно присоединить к своему проекту.


    Линковка может быть статическая и динамическая. Статическая представляет собой сбор *.obj-файлов библиотеки в *.lib, который мы можем, указав линкеру, прикрепить к нашей программе в момент компиляции.
    Содержимое библиотеки, как всегда, описывается в хидерах, которые распространяются вместе с *.lib . На выходе мы получим одинокий исполняемый файл вашей программы (*.exe).
    Динамическая линковка выполняется средствами платформы (операционной системы) в процессе работы программы. Все так же у нас в руках *.lib и *.h файлы, однако, теперь к ним добавляется *.dll. *.lib-файл теперь содержит только описания содержимого *.dll, где уже непосредственно находятся алгоритмы и ресурсы. Потому теперь выходные файлы проекта - это *.exe + *.dll .
    Несколько программ могут использовать один *.dll одновременно, тем самым не занимая оперативную память одинаковыми кусками и сами программы меньше размером. Так, например, работают многие драйверы и графические библиотеки (DirectX и OpenGL).

    Статическая компоновка


    Пусть у нас есть простой проект с одним классом, который мы хотим передать клиенту в двоичном виде: class SomeType { // Header.h int i; public: SomeType(); SomeType& inc (int value = 1); int get(); }; // И его реализация: // Implementation.cpp #include "Header.h" SomeType::SomeType() : i(0){ } SomeType& SomeType::inc( int value /* = 1*/ ) { i += value; return *this; } int SomeType::get() { return i; }


    Открыть мастер классических приложений и выбрать тип приложения LIB


    Создаем другой проект в этом же решении (solution) – консольное приложение
    И вставляем туда это код:
    //----------------------------- #include #include #include "Header.h" // Обращаемся к хидеру в проекте LIB using std::cout; using std::endl;
    #pragma comment (lib, "testLibDll.lib") // указываю на связанную lib
    int main() {
    SomeType st; st.inc().inc(3).inc(2); cout << "Result is " << st.get() << endl; return 1; }


    // кроме того настраиваем решение: делаем Exe–проект запускаемым и добиваемся, чтобы хидер и lib библиотеки были видны. Настраиваем дополнительные пути для компилятора и линкера.

    Динамическая компоновка


    Операции - аналогичны статической линковке за исключением того, что теперь исполняемый файл не будет запускаться без находящейся в этом же каталоге (или Windows или System32) *.dll .
    Для компиляции динамической библиотеки создаем новый проект, компилируем его и указываем dll путь, где находится исполняемый файл клиентской программы или в %windir%\System32 (операции же с хидерами и lib те же, что и при статической линковке).


    Чтобы правильно передавать функции, величины и типы из DLL в EXE необходимо сделать следующее (Студия сама это сделает при создании проекта и даже все объяснит):
    Приведенный ниже блок ifdef — это стандартный метод создания макросов, упрощающий процедуру экспорта из библиотек DLL. Все файлы данной DLL скомпилированы с использованием символа MYDLL_EXPORTS символ, определенный в командной строке.
    Этот символ не должен быть определен в каком-либо проекте, использующем данную DLL.
    Благодаря этому любой другой проект, исходные файлы которого включают данный файл, видит функции MYDLL_API как импортированные из DLL, тогда как данная DLL видит символы, определяемые данным макросом, как экспортированные.

    #ifdef MYDLL_EXPORTS


    #ifdef MYDLL_EXPORTS
    #define MYDLL_API __declspec(dllexport)
    #else
    #define MYDLL_API __declspec(dllimport)
    #endif
    // Этот класс экспортирован из библиотеки DLL
    class MYDLL_API CMyDll {
    public:
    CMyDll(void);
    };
    extern MYDLL_API int nMyDll; // экспорт значения
    MYDLL_API int fnMyDll( void); // экспорт функции


    Хидер на предыдущем слайде будет по-разному интерпретирован.
    В DLL-приложении будет действовать этот макрос:
    MYDLL_API __declspec(dllexport)
    а в EXE-приложении макрос импорта:
    #define MYDLL_API __declspec(dllimport)


    Опять размещаем код. Объявления – в хидере, определения – в cpp
    // Header1.h
    class MYDLL_API SomeType { // Header.h: добавлено MYDLL_API int i; public: SomeType(); SomeType& inc (int value = 1); int get(); };
    // cpp #include "Header1.h" SomeType::SomeType() : i(0){ } SomeType& SomeType::inc(int value /* = 1*/ ) { i += value; return *this; } int SomeType::get() { return i; }


    Ну и делаем вызов функций, расположенных в DLL
    #include #include #include "Header1.h" // Обращаемся к хидеру в проекте DLL using std::cout; using std::endl;
    #pragma comment (lib, "MyDll.lib") // указываю на внешнюю DLL через ее lib
    int main() {
    SomeType st; st.inc().inc(3).inc(2); cout << "Result is " << st.get() << endl; return 1; }

    Еще один способ динамической компоновки


    Можно сделать так, чтобы DLL загружать (и выгружать) в приложение ручным образом, то есть непосредственно
    HINSTANCE h = 0; typedef int (*Multiplies)(int x ); // тип указателя на функцию
    h = LoadLibrary ("MyDll.dll"); mm = reinterpret_cast( GetProcAddress (h, "fnMyDll"));
    Но для этого способа нужно еще добавить в проект Dll def-файл
    Это файл определения модуля


    И в нем прописываем:
    /// - def-файл
    LIBRARY "MyDll.dll"
    EXPORTS
    fnMyDll @1
    … // еще какие-то функции – у каждой свой номер
    // конец def-файла
    Но класс так передать нельзя.
    Можно посмотреть подробнее на
    https://docs.microsoft.com/ru-ru/cpp/build/exporting-from-a-dll-using-def-files?view=msvc-160


    В следующий раз планируется рассмотреть создание Windows- приложений с применением .NET.

    Домашнее задание на неделю


    Проект 2.
    Есть такой список источников (из Википердии):
    1. Stolz, M. (2002), "The History Of Applied Mathematics And The History Of Society", Synthese, 133 (1): 43–57, doi:10.1023/A:1020823608217
    2. Von Zur Gathen, J., & Gerhard, J. (2013). Modern computer algebra. Cambridge University Press.
    3. Geddes, K. O., Czapor, S. R., & Labahn, G. (1992). Algorithms for computer algebra. Springer Science & Business Media.
    4. Albrecht, R. (2012). Computer algebra: symbolic and algebraic computation (Vol. 4). Springer Science & Business Media.
    5. Mignotte, M. (2012). Mathematics for computer algebra. Springer Science & Business Media.
    6. Conte, S. D., & De Boor, C. (2017). Elementary numerical analysis: an algorithmic approach. Society for Industrial and Applied Mathematics.
    7. Greenspan, D. (2018). Numerical Analysis. CRC Press.
    8. Linz, P. (2019). Theoretical numerical analysis. Courier Dover Publications.
    9. Greenberg, John L.; Goodstein, Judith R. (1983-12-23). "Theodore von Kármán and Applied Mathematics in America" Science. 222 (4630): 1300–1304. doi:10.1126/science.222.4630.1300


    Этот список первоначально надо разбить на два класса – книги и статьи. И БД для их хранения и извлечения.
    Создать проект классического Windows приложения EXE и проект DLL в едином решении (solution).
    База данных должна использовать map.
    Все классы реализовать в DLL.
    В проекте EXE, в WinMain реализовать загрузку списка источников в БД.
    Добавить в меню обработчики событий: сортировка по номеру, по DOI (если дои отсутствует – это пустое поле), по фамилии 1-го автора и по году. По каждому отклику следует вывести в окно соответствующим образом отсортированный список – лучше в виде таблицы с указанием столбцов.
    Кроме того, еще программа должна печатать только книги или только статьи.
    Проявите смекалку, что в списке – книга, а что – статья.
    (можете делать диалоговые приложения или еще какие-то, если знаете, – полная свобода выбора)


    Пусть есть класс базы данных, а внутри:
    map < string Фамилия_строка , Фамилия_класс*, pred> m_map;
    Реализовать предикат pred так, чтобы внутри map всё было отсортировано по 3-ей букве и если они совпадают, то каждой последующей до конца строки букве в названии фамилии.


    Контрольная работа 3



    написать администратору сайта