воскресенье, 27 ноября 2011 г.

Пример использования SQLite3

В примере используется функция wxLogMessage из библиотеки wxWidgets для вывода на экран, можно ее заменить printf, убрав wxT, суть не в этом, только надо помнить, что SQLite3 библиотека использует Unicode (UTF-8).


    sqlite3 *db;
    int rc;

    rc = sqlite3_open("test.sqlite", &db);

    if( rc )
    {
        wxLogMessage(wxT("Can't open database: %s"), sqlite3_errmsg(db));
        sqlite3_close(db);
        exit(1);
    }

    sqlite3_stmt *pStmt;
    char zSql[] = "SELECT * From tbl;";

    do
    {
        /* Компилираем запрос SQL. Ожидаем успех.*/
        sqlite3_prepare(db, zSql, -1, &pStmt, 0);

        while( SQLITE_ROW==sqlite3_step(pStmt) )
        {
            /* Что-то делаем с записью */
            wxString  str = wxString((const char*)sqlite3_column_text(pStmt, 1), wxConvUTF8);

            wxLogMessage(wxT("%d | %s"),sqlite3_column_int(pStmt,0),str.c_str());

        }

        /* Завершаем запрос. Если ошибка SQLITE_SCHEMA
        ** произошло, то вышестоящий вызов sqlite3_step()
        ** вернул SQLITE_ERROR. sqlite3_finalize() вернет
        ** SQLITE_SCHEMA. В этом случае повторяем цикл.
        */
        rc = sqlite3_finalize(pStmt);
    }
    while( rc==SQLITE_SCHEMA );

    wxString Sql;

    Sql=wxT("Select * From tbl;");

    do
    {
        /* Компилираем запрос SQL. Ожидаем успех. */
        sqlite3_prepare(db, Sql.ToUTF8(), -1, &pStmt, 0);

        while( SQLITE_ROW==sqlite3_step(pStmt) )
        {
            /* Что-то делаем с записью */
            wxString  str = wxString((const char*)sqlite3_column_text(pStmt, 1), wxConvUTF8);

            wxLogMessage(wxT("%d | %s"), sqlite3_column_int(pStmt,0),str.c_str());
        }

        /* Завершаем запрос. Если ошибка SQLITE_SCHEMA
        ** произошло, то вышестоящий вызов sqlite3_step()
        ** вернул SQLITE_ERROR. sqlite3_finalize() вернет
        ** SQLITE_SCHEMA. В этом случае повторяем цикл.
        */
        rc = sqlite3_finalize(pStmt);
    }
    while( rc==SQLITE_SCHEMA );

    sqlite3_close(db);

среда, 16 ноября 2011 г.

Вирус - вымогатель

На одном из компов при входе под пользователем, появилось сообщение угрожающего содержания.
Что делать?
Уходим в спящий режим. Загружаемся.
Входим под другим пользователем с правами администратора.
Запускаем диспетчер задач.
Включаем галочку "Отображать процессы всех пользователей".
Находим подозрительную задачу нашего неосторожного пользователя и завершаем её.
Меняем пользователя, заходим под проблемным пользователем и запускаем диспетчер задач.
В меню диспетчера задач выбираем Выполнить и набираем "Мои документы" или explorer, чтоб панель задач появилась и другие сомнительные удобства Windows.

Лечим, ...

понедельник, 14 ноября 2011 г.

Создание базы sqlite3 из командного файла

Имею файл db.sql, сожержащий команды для консольного sqlite3-клиента.
В Windows делаю так:

type db.sql | sqlite3 database.db

Эти команды можно прописать в командном файле для cmd.

В ОС семейства Unix можно выполнить:

cat db.sql | sqlite3 database.db


Более универсально, делать так

sqlite3 database.db < db.sql


В db.sql можно запихнуть, в принципе, достаточно сложные команды, да и сам командный файл можно сделать не тривиалным, если знать команды оболочки и SQL.

пятница, 4 ноября 2011 г.

Обобщенная Windows программа

(перевел, как смог http://www.relisoft.com/win32/generic.html)
Эта программа использует основной набор классов инкапсулирующих Windows API.
Controller - мост между оконной процедурой и объектно-ориентированным миром.
View - инкапсулирует вывод; Windows программы.
Canvas - инкапсулирует различные контексты устройств и то, что вы можете делать с ним.
Model - руки и мозг вашей программы, которые не имеют дело с Windows совсем.
Давайте начнем с WinMain, там мы создаем оконный класс и окно верхнего уровня нашего приложения. Я скрою действия внутри двух классов: WinClass и WinMaker. WinClass может сказать нам, о том, что уже запущен экземпляр; нашей программы. Когда нечто подобное случается, в нашем примере, мы можем легко активировать подготовленный к запуску экземпляр приложения и выйти. Вы можете делать это, тогда когда захотите, только один экземпляр вашей программы может быть запущен за раз.
Раз окно верхнего уровня прекрасно создано, то мы входим в цикл (петлю) обработки сообщений. Извещение о том, что в этот раз мы обрабатываем клавиатурные вызовы, получаем посредством вызова TranslateMessage. Поскольку наша программа имеет пункты меню, то должно быть доступно использование Alt+[клавиша] комбинаций.
Другая интересная черта этой программы - это то, что мы не будем использовать строки в названии наших ресурсов - мы используем числовые идентификаторы. Более того, когда происходит API вызов для получения строки, наподобие имени оконного класса или заголовка, мы храним строки в строковых ресурсах и доступ к ним получаем через идентификаторы (ids). Ваша среда разработки под Windows очень вероятно имеет редактор ресурсов, что позволяет создавать иконки, меню, и строковые ресурсы и определять им подходящие числовые идентификаторы. Символические имена для этих идентификаторов хранятся в заголовочном файле, созданном таким редактором, в нашем случае - это resource.h.
Постоянная ID_MAIN, например, относится к главным иконкам приложения (большая и маленькая в некотором ресурсе), главному меню, и строке с именем оконного класса. ID_CAPTION относится к строке заголовка окна. Такая организация расширяет возможности повторного использования кода, не говоря уж об удобстве локализации.


int WINAPI WinMain
(HINSTANCE hInst, HINSTANCE hPrevInst,
 char * cmdParam, int cmdShow)
{
    _set_new_handler (& NewHandler);
 
// Использование исключений поможет отлаживать вашу программу
// и защитить от неожиданных инцидентов.
    try
    {
// Создание класса окна верхнего уровня
        TopWinClass topWinClass (ID_MAIN, hInst, MainWndProc);
// Действительно ли запущен экземпляр этой программы?
        HWND hwndOther = topWinClass.GetRunningWindow ();
        if (hwndOther != 0)
        {
            ::SetForegroundWindow (hwndOther);
            if (::IsIconic (hwndOther))
                ::ShowWindow (hwndOther, SW_RESTORE);
            return 0;
        }
        topWinClass.Register ();
 
// Создаем окно верхнего уровня
        ResString caption (hInst, ID_CAPTION);
        TopWinMaker topWin (topWinClass, caption);
        topWin.Create ();
        topWin.Show (cmdShow);
// Главная петля сообщений
        MSG msg;
        int status;
        while ((status = ::GetMessage (&msg, 000)) != 0)
        {
            if (status == -1)
                return -1;
            ::TranslateMessage (&msg);
            ::DispatchMessage (&msg);
        }
 
        return msg.wParam;
    }
    catch ( WinException e )
    {
        char buf [50];
        wsprintf (buf, "%s, Error %d", e.GetMessage (), e.GetError ());
        ::MessageBox (0, buf, "Exception", MB_ICONEXCLAMATION | MB_OK);
    }
    catch (...)
    {
        ::MessageBox (0, "Unknown", "Exception", 
                      MB_ICONEXCLAMATION | MB_OK);
    }
 
    return 0;
}
 

Давайте взглянем на класс WinClass. Он инкапсулирует вызов Windows-определенной структуры WNDCLASSEX и обеспечивает приемлемые значения по умолчанию для всех его полей. Этот класс является производным от простого класса WinSimpleClass, который вы можете использовать для встраивания некоторых Windows классов (подобных кнопкам, спискам и т.д.).
Я определил, примерные методы, которые могут быть использованы для переопределения значений по умолчанию. Например, SetBgSysColor изменяет цвет фона определенный по умолчанию в клиентской области окна в один из предопределенных системой цвет. Метод SetResIcons загружает соответствующие иконки из ресурсов и закрепляет их за оконным классом. Эти иконки будут потом появляться в верхнем левом углу главного окна и на панели задач Windows.
TopWinClass производный от WinClass позволяет использовать его методы. Он также устанавливает меню вверху оконного класса.

class WinSimpleClass
{
public:
     WinSimpleClass (char const * name, HINSTANCE hInst)
        : _name (name), _hInstance (hInst)
    {}
    WinSimpleClass (int resId, HINSTANCE hInst);
    char const * GetName () const
    {
        return _name.c_str ();
    }
    HINSTANCE GetInstance () const
    {
        return _hInstance;
    }
    HWND GetRunningWindow ();
protected:
     HINSTANCE _hInstance;
    std::string _name;
};
WinSimpleClass::WinSimpleClass (int resId, HINSTANCE hInst)
    : _hInstance (hInst)
{
    ResString resStr (hInst, resId);
    _name = resStr;
}
HWND WinSimpleClass::GetRunningWindow ()
{
    HWND hwnd = ::FindWindow (GetName (), 0);
    if (::IsWindow (hwnd))
    {
        HWND hwndPopup = ::GetLastActivePopup (hwnd);
        if (::IsWindow (hwndPopup))
            hwnd = hwndPopup;
    }
    else 
        hwnd = 0;
    return hwnd;
}
class WinClass: public WinSimpleClass
{
public:
    WinClass (char const * className, HINSTANCE hInst, WNDPROC wndProc);
    WinClass (int resId, HINSTANCE hInst, WNDPROC wndProc);
    void SetBgSysColor (int sysColor)
    {
        _class.hbrBackground = reinterpret_cast (sysColor + 1);
    }
    void SetResIcons (int resId);
    void Register ();
protected:
    void SetDefaults ();
    WNDCLASSEX _class;
};
WinClass::WinClass (char const * className, 
                     HINSTANCE hInst, 
                     WNDPROC wndProc)
    : WinSimpleClass (className, hInst)
{
    _class.lpfnWndProc = wndProc;
    SetDefaults ();
}
WinClass::WinClass (int resId, HINSTANCE hInst, WNDPROC wndProc)
    : WinSimpleClass (resId, hInst)
{
    _class.lpfnWndProc = wndProc;
    SetDefaults ();
}
void WinClass::SetDefaults ()
{
// Provide reasonable default values
    _class.cbSize = sizeof (WNDCLASSEX);
    _class.style = 0;
    _class.lpszClassName = GetName ();
    _class.hInstance = GetInstance ();
    _class.hIcon = 0;
    _class.hIconSm = 0;
    _class.lpszMenuName = 0;
    _class.cbClsExtra = 0;
    _class.cbWndExtra = 0;
    _class.hbrBackground = reinterpret_cast (COLOR_WINDOW + 1);
    _class.hCursor = ::LoadCursor (0, IDC_ARROW);
}
void WinClass::SetResIcons (int resId)
{
    _class.hIcon = reinterpret_cast (
                       ::LoadImage (
                           _class.hInstance, 
                           MAKEINTRESOURCE (resId), 
                           IMAGE_ICON, 
                           ::GetSystemMetrics (SM_CXICON),
                           ::GetSystemMetrics (SM_CYICON),
                           0));
// Small icon can be loaded from the same resource
    _class.hIconSm = reinterpret_cast (
                         ::LoadImage (
                             _class.hInstance, 
                             MAKEINTRESOURCE (resId), 
                             IMAGE_ICON, 
                             ::GetSystemMetrics (SM_CXSMICON),
                             ::GetSystemMetrics (SM_CYSMICON),
                             0));
}
void WinClass::Register ()
{
    if (::RegisterClassEx (&_class) == 0)
        throw WinException ("Internal error: RegisterClassEx failed.");
}
class TopWinClass: public WinClass
{
public:
    TopWinClass (int resId, HINSTANCE hInst, WNDPROC wndProc);
};
TopWinClass::TopWinClass (int resId, 
                           HINSTANCE hInst, WNDPROC wndProc)
    : WinClass (resId, hInst, wndProc)
{
    SetResIcons (resId);
    _class.lpszMenuName = MAKEINTRESOURCE (resId);
}
 

Од;нажды зарегистрировав в системе Windows класс, вы можете создать много окон этого класса, если только захотите. Они, конечно, будут совместно использовать некоторую оконную процедуру, которая была зарегистрированная вместе с классом. Мы увидим позже, как можно различать экземпляры окна внутри процедуры.
Класс WinMaker работает в большинстве случаев как WinClass. Его конструктор обеспечивает разумные значения по умолчанию, которые могут быть переопределены посредством вызова специальных методов. Как только все определено, вы вызываете метод Create, который создает окно и метод Show для его отображения. Внимание, в тот момент, когда вы вызываете Create, ваша оконная процедура вызывается с сообщением WM_CREATE .
Окно верхнего уровня, созданное с использованием класса TopWinMaker, который обеспечивает соответствующий стиль и надпись.

class WinMaker
{
public:
    WinMaker (WinClass & winClass);
    operator HWND ()
    {
        return _hwnd;
    }
    void AddCaption (char const * caption)
    {
        _windowName = caption;
    }
    void AddSysMenu ()
    {
        _style |= WS_SYSMENU;
    }
    void AddVScrollBar ()
    {
        _style |= WS_VSCROLL;
    }
    void AddHScrollBar ()
    {
        _style |= WS_HSCROLL;
    }
    void Create ();
    void Show (int nCmdShow = SW_SHOWNORMAL);
protected:
    WinClass & _class;
    HWND _hwnd;
    DWORD _exStyle; // extended window style
    char const * _windowName; // pointer to window name
    DWORD _style; // window style
    int _x; // horizontal position of window
    int _y; // vertical position of window
    int _width; // window width
    int _height; // window height
    HWND _hWndParent; // handle to parent or owner window
    HMENU _hMenu; // handle to menu, or child-window ID
    void * _data; // pointer to window-creation data
};
WinMaker::WinMaker (WinClass & winClass)
    : _hwnd (0),
    _class (winClass),
    _exStyle (0), // extended window style
    _windowName (0), // pointer to window name
    _style (WS_OVERLAPPED), // window style
    _x (CW_USEDEFAULT), // horizontal position of window
    _y (0), // vertical position of window
    _width (CW_USEDEFAULT), // window width
    _height (0), // window height
    _hWndParent (0), // handle to parent or owner window
    _hMenu (0), // handle to menu, or child-window identifier
    _data (0) // pointer to window-creation data
{
}
void WinMaker::Create ()
{
    _hwnd = ::CreateWindowEx (
                _exStyle,
                _class.GetName (),
                _windowName,
                _style,
                _x,
                _y,
                _width,
                _height,
                _hWndParent,
                _hMenu,
                _class.GetInstance (),
                _data);
    if (_hwnd == 0)
        throw WinException ("Internal error: Window Creation Failed.");
}
void WinMaker::Show (int nCmdShow)
{
    ::ShowWindow (_hwnd, nCmdShow);
    ::UpdateWindow (_hwnd);
}
// Makes top overlapped window with caption
class TopWinMaker: public WinMaker
{
public:
    TopWinMaker (WinClass & winClass, char const * caption);
};
TopWinMaker::TopWinMaker ((WinClass & winClass, char const * caption)
                               : WinMaker (winClass)
{
    _style = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
    _windowName = caption;
}
 

Перед тем как мы пойдем дальше, здесь будут некоторые легкие классы для общей выгоды. WinException - это примерно, то, что мы хотим, он выбросит исключение в любом случае при неудаче с Windows API. Он позаботится о возврате кода ошибки Windows. Для конвертации кода ошибки в строку используется FormatMessage API.
Класс ResString является простой l0;нкапсуляцией хранения строковых ресурсов вашего приложения.

// Класс исключения: сохраняет сообщение и код ошибки
class WinException
{
public:
    WinException (char* msg)
        : _err (::GetLastError()), _msg(msg)
    {}
    DWORD GetError() const
    {
        return _err;
    }
    char const * GetMessage () const
    {
        return _msg;
    }
private:
    DWORD _err;
    char * _msg;
};
 
// Выход за пам 103;ть дескриптора: выбрасывает исключение
int NewHandler (size_t size)
{
    throw WinException ( "Out of memory" );
    return 0;
}
 
class ResString
{
    enum { MAX_RESSTRING = 255 };
public:
    ResString (HINSTANCE hInst, int resId);
    operator char const * ()
    {
        return _buf;
    }
private:
    char _buf [MAX_RESSTRING + 1];
};
 
ResString::ResString (HINSTANCE hInst, int resId)
{
    if (!::LoadString (hInst, resId, _buf, MAX_RESSTRING + 1))
        throw WinException ("Load String failed");
}
 

Contriller (контроллер) - это нервная система исключительно оконного экземпляра. Он создается вместе с окном, сохраняется с ним и разрушается с ним. Вы можете определить любую информацию характерную для экземпляра окна в его контроллере. В общем, контроллер связан с представлением (view), которое отрисовывает на поверхности окна и он имеет доступ к модели (model), которая является мозгом вашего приложения (это называют MVC, или Model-View-Controller паттерн, изобретенный Smalltalk программистами).
Как это часто бывает, ваше приложение имеет только одно окно верхнего уровня, вы можете непосредственно включить модель (model) в его контроллер (controller). Это упрощает управление ресурсами, но там возникают накладные расходы на соединение контроллера с моделью. В крупных проектах избегают таких соединений — используют (умный) указатель на модель внутри контроллера, что предпочтительно.
Большинство методов контроллера зависят от дескриптора окна, которым они оперируют. Это дескриптор передается с каждым сообщением Windows, но его проще хранить один раз внутри объекта контроллера и использовать его всякий раз когда необходимо. Помните, они находятся в отношении один-к-одному, соответственно между окном экземпляра (и следовательно дескрипторами окна) и объектами контроллера.

class Controller
{
public:
    Controller(HWND hwnd, CREATESTRUCT * pCreate);
    ~Controller ();
    void Size (int x, int y);
    void Paint ();
    void Command (int cmd);
 
private:
 
    HWND _hwnd;
 
    Model _model;
    View _view;
};

Оконная процедура - это главный коммутатор приложения Windows. Вы не вызываете ее из; вашей программы - Windows делает это! Кажды;й раз что-то интересное происходит, Windows отправляет вашей программе сообщения. Это сообщение передается в вашу оконную процедуру. Вы можете обработать его, или вы можете передать его оконной процедуре по умолчанию.
Оконная процедура вызывается с дескриптором окна, которому переданное сообщение направлено. Этот дескриптор однозначно идентифицирует внутреннюю структуру данных Windows, которая соответствует переданному экземпляру окна. К стати, GWL_USERDATA часть этой структуры гарантированно представлена во всех окнах, включая окна сообщений (MessageBox), диалоги и кнопки.

template 
inline T WinGetLong (HWND hwnd, int which = GWL_USERDATA)
{
    return reinterpret_cast (::GetWindowLong (hwnd, which));
}
 
template 
inline void WinSetLong (HWND hwnd, T value, int which = GWL_USERDATA)
{
    ::SetWindowLong (hwnd, which, reinterpret_cast (value));
}

Каждый раз Windows вызывает нашу оконную процедуру, мы должны вначале определить объект-контроллер. Помните, что может быть несколько окон разделяющих некоторую оконную процедуру, и мы желаем иметь отдельный контроллер для каждого окна. Как можем мы узнать, какой контроллер использовать, когда мы делаем вызов? Мы находим его посредством дескриптора окна. В этом дескрипторе мы храним указатель на контроллер окна, используем Win[Set/Get]Long функций.
При первом вызове, оконная процедура вызывается с WM_CREATE сообщением. В этот время мы создаем объект контроллер и инициализируем его дескриптором окна и специальной структурой данных называемой CREATESTRUCT, которую мы передаём в распор;яжение Windows. Как только мы имеем контроллер, мы сохраняем указатель на него в соответствующую внутреннюю структуру данных Windows под меткой текущего hwnd (дескриптор окна). В следующий раз оконная процедура вызывается с сообщением отличным от WM_CREATE, мы просто находим указатель на наш контроллер, используя hwnd.
Спокойно отдохнем. Оконная процедура интерпретирует параметры сообщения и вызывает соответствующие методы контроллера.

LRESULT CALLBACK WndProc
(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    Controller * pCtrl = WinGetLong (hwnd);
 
    switch (message)
    {
    case WM_CREATE:
// Ловим исключение в случае нового исключения!
        try
        {
            pCtrl = new Controller (hwnd, 
                                    reinterpret_cast (lParam));
            WinSetLong (hwnd, pCtrl);
        }
        catch (WinException e)
        {
            ::MessageBox (hwnd, e.GetMessage(), "Initialization",
                          MB_ICONEXCLAMATION | MB_OK);
            return -1;
        }
        catch (...)
        {
            ::MessageBox (hwnd, "Unknown Error", "Initialization",
                          MB_ICONEXCLAMATION | MB_OK);
            return -1;
        }
        return 0;
    case WM_SIZE:
        pCtrl->Size (LOWORD(lParam), HIWORD(lParam));
        return 0;
    case WM_PAINT:
        pCtrl->Paint ();
        return 0;
    case WM_COMMAND:
        pCtrl->Command (LOWORD (wParam));
        return 0;
    case WM_DESTROY:
        WinSetLong (hwnd, 0);
        delete pCtrl;
        return 0;
    }
    return ::DefWindowProc (hwnd, message, wParam, lParam);
}

Здесь примеры простого выполнения нескольких методов контроллера. Конструктор запоминает дескриптор окна для дальнейшего использования, деструктор отправляет сообщение о выходе. Метод Size передает его аргумент в View, и т. д. Мы говорим об отрисовке окна немного позже. Теперь, заметим, что контроллер подготавливает область рисования для работы View.

Controller::Controller (HWND hwnd, CREATESTRUCT * pCreate)
    :_hwnd (hwnd), 
    _model ("Generic")
{
}
 
Controller::~Controller ()
{
    ::PostQuitMessage(0);
}
 
void Controller::Size (int cx, int cy)
{
    _view.SetSize (cx, cy);
}
 
void Controller::Paint ()
{
// подготавливаем холст и передаём View
    PaintCanvas canvas (_hwnd);
    _view.Paint (canvas, _model);
// Заметим: деструктор PaintCanvas вызывается автоматически!
}

Когда пользователь выбирает один из пунктов меню, оконная процедура вызывается с сообщением WM_COMMAND. Соответствующий метод контроллера отправляет команду, зависящую от идентификатора (id). Когда вы создаете меню используя ваш редактор ресурсов, вы выбираете эти командные идентификаторы для каждого пункта меню. Они хранятся в соответствующем заголовочном файле (resource.h в нашем случае), он должен быть включен в исходник контроллера.
Наше меню содержит только три пункта с идентификаторами IDM_EXIT, IDM_HELP, и IDM_ABOUT. Диалог, который отображается в ответ на IDM_ABOUT, также создан с использованием редактора ресурсов, и ему дан идентификатор IDD_ABOUT. Его диалоговая процедура - AboutDlgProc.
Наконец, для нормального отображения диалога мы нуждаемся в дескрипторе экземпляра приложения. Обычно, доступ к нему получаn2;т через внутреннюю структуру данных Windows, использованием соответствующего hwnd.

// Обработка команд меню
 
void Controller::Command (int cmd)
{
    switch (cmd)
    {
    case IDM_EXIT:
        ::SendMessage (_hwnd, WM_CLOSE, 00L);
        break;
    case IDM_HELP:
        ::MessageBox (_hwnd, "Go figure!",
                      "Generic", MB_ICONINFORMATION | MB_OK);
        break;
    case IDM_ABOUT:
    {
// Дескриптор экземпляра приложения доступен черехз HWND
        HINSTANCE hInst = WinGetLong (_hwnd, 
                          GWL_HINSTANCE);
        ::DialogBox (hInst, 
                     MAKEINTRESOURCE (IDD_ABOUT), 
                     _hwnd, 
                     AboutDlgProc);
    }
    break;
    }
}

Объект представления обычно хранит параметры клиентской области. Они обновляются всякий раз, когда процессы контроллера сообщают WM_SIZE. Первое WM_SIZE сообщение посылается вовремя создания и перед WM_PAINT, так мы можем быть уверены, что к моменту вызова Paint, параметры клиентской области известны.
Графический вывод в окно сделан, посредством вызова соответствующих методов объекта Canvas. В нашем случае, мы отрисовываем текст получаемый из модели и рисуем вертикальную линию на 10 пикселей левее от края клиентской области.

class View
{
public:
 
    void SetSize (int cxNew, int cyNew)
    {
        _cx = cxNew;
        _cy = cyNew;
    }
 
    void Paint (Canvas & canvas, Model & model);
 
protected:
 
    int _cx;
    int _cy;
};
 
void View::Paint (Canvas & canvas, Model & model)
{
    canvas.Text (121, model.GetText(), model.GetLen());
    canvas.Line (10010, _cy);
}
 

Объект холста инкапсулирует, то что называется контекстом устройства (Device Context) в языке Windows. Наш Canvas - это очень прост, он только знает как печатать текст и рисовать линии, но ваш Canvas может иметь на много больше методов, которые делают творческие вещи. 

class Canvas
{
public:
    operator HDC ()
    {
        return _hdc;
    }
 
    void Line ( int x1, int y1, int x2, int y2 )
    {
        ::MoveToEx (_hdc, x1, y1, 0);
        ::LineTo (_hdc, x2, y2);
    }
 
    void Text (int x, int y, char const * buf, int cBuf)
    {
        ::TextOut ( _hdc, x, y, buf, cBuf );
    }
 
    void Char (int x, int y, char c)
    {
        ::TextOut (_hdc, x, y, & c, 1);
    }
 
protected:
 
// Защищенный конструктор: Вы не можете строить
// объект типа Canvas, но вам доступно наследование его.
     Canvas (HDC hdc): _hdc (hdc) {}
 
    HDC _hdc;
};

Холст (canvas), что вы создаете, в ответ на сообщение WM_PAINT может быть специально изменен. Этот контекст устройства захватывается вызовом BeginPaint и освобождается вызовом EndPaint. PAINTSTRUCT содержит дополнительную информацию о каждой части клиентской области, которая отрисована и др. Здесь мы игнорируем некоторые детали, но если это важно для вас вы могли бы узнать больше об этом.

// Concrete example of canvas.
// Создаем этот объект после сообщения WM_PAINT
 
class PaintCanvas: public Canvas
{
public:
// Конструктор получает DC
    PaintCanvas (HWND hwnd)
        : Canvas (::BeginPaint (hwnd, & _paint)),
        _hwnd (hwnd)
    {}
 
// Деструктор освобождает DC
    ~PaintCanvas ()
    {
        ::EndPaint(_hwnd, & _paint);
    }
 
protected:
 
    PAINTSTRUCT _paint;
    HWND _hwnd;
};
 

Хотели бы вы программировать без контролов?