среда, 7 декабря 2011 г.

Алгоритмическое мышление (заповеди программиста)

Алгоритмическое мышление - это слабое место, многих начинающих программировать людей. Тренировать его можно, просто решая задачки, головоломки, играя в шахматы и др. Математика помогает в этом, безусловно.
В общем-то арсенал конструкций для составления алгоритмов не велик:
- следование;
- ветвление;
- циклы (с предусловием, с постусловием и параметризованный).
Немного математики, знание функций и операций для конкретной среды программирования и на выходе получаем программки, при условии наличия и использования мозга.
Для языка Си основные моменты, необходимые для создания простых консольных программ, можно при желании разместить на одной странице.
Алгоритмическое мышление - это основной инструмент программиста. Этот аппарат, надо холить и лилееть, материальным носителем его является "серое вещество", следовательно мозги надо держать в тонусе и бережно к ним относится. В некотором смысле, мозг работает как мышца, ей нужна тренировка, отдых и питание. Пагубны для "серого вещества" алкоголь и табак, а так же их аналоги (наркотик, кальян, психотропные лекарства и др.).
В здоровом теле - здоровый дух.
Есть и другие факторы, мешающие раскрытию потенциала "серого вещества", например, СМИ (особенно если они выполняют, чей-то политический или др. заказ), развлекательные шоу и др., по сути, они создают информационный шум, будоража воображение, отвлекая и препятствуя ясности мышления.
Понятно, что для того, чтобы преуспеть в программировании, да и в других областях, где нужно мышление как основной инструмент, надо придерживаться некоторых правил, т. е. соответствующего образа жизни. Подозреваю, что эти правила будут иметь некоторое существенное сходство с уставом монастыря, в части, которая связана с духовным развитием.
Хорошо бы выработать десятку правил, которые оберегали бы тонкую нервную организацию программистов (типа заповедей программиста).

воскресенье, 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, 0, 0, 0)) != 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, 0, 0L);
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 (12, 1, model.GetText(), model.GetLen());
canvas.Line (10, 0, 10, _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;
};


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

пятница, 23 сентября 2011 г.

Простейшая Windows программа

Простейшая Windows программа

(как смог, так и перевел http://relisoft.com/win32/winnie.html)

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

Вызовы Windows API выделены синим, а определенные в Windows типы данных показаны зеленым. Я буду обычно ставить двойное двоеточие в API вызовах. В C++, это просто способ вызова глобальных функций, на случай избегания двусмысленностей.

Для проверки работоспособности кода надо создать проект типа Win32 Application.

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

Объявление оконной процедуры (WindowProcedure). Windows вызывает ее с дескриптором окна (handle), сообщением (message) и двумя переменными (данные сообщения) связанными с сообщением, параметры типа WPARAM и LPARAM.

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

Как только все поля WINDCLASS заполнены, мы регистрируем класс в системе Windows.

#include <windows.h>
 
 
LRESULT CALLBACK WindowProcedure
    (HWND hwnd, unsigned int message, WPARAM wParam, LPARAM lParam);
 
class WinClass
{
public:
    WinClass (WNDPROC winProc, char const * className, HINSTANCE hInst);
    void Register ()
    {
        ::RegisterClass (&_class);
    }
private:
    WNDCLASS _class;
};
 
WinClass::WinClass
    (WNDPROC winProc, char const * className, HINSTANCE hInst)
{
    _class.style = 0;
    _class.lpfnWndProc = winProc; // обязательная оконная процедура
    _class.cbClsExtra = 0;
    _class.cbWndExtra = 0;
    _class.hInstance = hInst;         // владелец класса
    _class.hIcon = 0;
    _class.hCursor = ::LoadCursor (0, IDC_ARROW); // опция
    _class.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); // опция
    _class.lpszMenuName = 0;
    _class.lpszClassName = className; // обязательно имя класса окна
}

Как только класс окна зарегистрирован мы можем создать окно. Это делается вызовом CreateWindow, принимающим следующие аргументы:

 имя класса окна, которое мы зарегистрировали; заголовок окна; стиль окна;  позиция; размер; экземпляр приложения. Эта часть программы может также быть заключена в C++ класс, WinMaker.

class WinMaker
{
public:
    WinMaker (): _hwnd (0) {}
    WinMaker (char const * caption, 
              char const * className,
              HINSTANCE hInstance);
    void Show (int cmdShow)
    {
        ::ShowWindow (_hwnd, cmdShow);
        ::UpdateWindow (_hwnd);
    }
protected:
    HWND _hwnd;
};
 
WinMaker::WinMaker (char const * caption, 
                    char const * className,
                    HINSTANCE hInstance)
{
    _hwnd = ::CreateWindow (
        className,            // имя зарегистрированного класса окна
        caption,              // заголовок окна
        WS_OVERLAPPEDWINDOW,  // стиль окна
        CW_USEDEFAULT,        // x позиция
        CW_USEDEFAULT,        // y позиция
        CW_USEDEFAULT,        // ширена
        CW_USEDEFAULT,        // высота
        0,                    // дескриптор родительского окна
        0,                    // дескриптор меню
        hInstance,            // дескриптор экземпляра приложения
        0);                   // данные окна
}

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

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

Все эти сообщения выстраиваются в очередь сообщений. Windows создает очередь сообщений для каждого запущенного приложения (фактически для каждого потока). Ваша обязанность отлавливать сообщения одно за другим в петле (цикле) сообщений. Ваша программа содержит вызов GetMessage извлекающий сообщение из очереди. Потом вы вызываете DispatchMessage передовая его обратно в Windows. Может ли Windows выполнятся дальше без задержки и доставки всех этих своих сообщений? В принципе это возможно, но петля сообщений дает вашей программе шанс посмотреть их и может быть совершать какие-либо дополнительные действия перед их отправкой. Или нет …

Каждое сообщение адресовано особому окну. Когда вы говорите Windows доставлять какое-либо сообщение, то это предполагает некоторый уникальный класс окна, поиск соответствующей оконной процедуры, и вызов ее. Каждый сигнал сообщение, посланное вашему окну, будет направлено вашей оконной процедуре. Которая будет отвечать на сообщение. Стоит ли отвечать на каждый возможный тип сообщения Windows? Сообщений может быть сотни. Так или иначе, мы передаем управление Windows, по умолчанию используя DefWindowProc.





 

Давайте взглянем на WinMain. Исполняемая в Windows программа не стартует с main, она стартует с WinMain. В нашей WinMain, мы создаем WinClass и регистрируем его. Потом мы создаем окно (класс которого только что зарегистрировали) и показываем его. Фактически, WinMain вызывается с соответствующей директивой показа, пользователь может захотеть стартовать приложение минимизированным или максимизированным. Далее, мы определяем петлю сообщений и продолжаем посылку сообщений до тех пор, пока GetMessage не вернет 0. В этом месте wParam сообщения будет содержать возвращаемый код программы в целом.

 

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst,
            char * cmdParam, int cmdShow)
{
    char className [] = "Winnie";
 
    WinClass winClass (WindowProcedure, className, hInst);
    winClass.Register ();
 
    WinMaker win ("Hello Windows!", className, hInst);
    win.Show (cmdShow);
 
    MSG  msg;
    int status;
 
    while ((status = ::GetMessage (& msg, 0, 0, 0)) != 0)
    {
        if (status == -1)
            return -1;
        ::DispatchMessage (& msg);
    }
 
    return msg.wParam;
}

 

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

 

// Оконная процедура вызываемая Windows
 
LRESULT CALLBACK WindowProcedure
    (HWND hwnd, unsigned int message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_DESTROY:
            ::PostQuitMessage (0);
            return 0;
 
    }
    return ::DefWindowProc (hwnd, message, wParam, lParam );
}

 

 

воскресенье, 3 июля 2011 г.

Model-View-Controller (MVC) + wxWidgets



Интерфейс "Наблюдатель" (Observer)



1 #ifndef OBSERVER_H
2 #define OBSERVER_H 1
3 class Observer
4 {
5 public:
6 virtual void update(){}
7 };
8 #endif




Описание модели (Model)



1 #ifndef MODEL_H
2 #define MODEL_H 1
3
4 #include <wx/string.h>
5 #include <set>
6
7 #include "Observer.h"
8
9 class Model
10 {
11 public:
12 Model(int d);
13 void setData(const int d);
14 int getData()const;
15 ~Model(){};
16 private:
17 int d_;
18 std::set<Observer*> registry;
19 public:
20 void attach(Observer*s);
21 void detach(Observer*s);
22 void notify();
23 // void clearRegistry();
24 };
25 #endif




Определение модели



1 #include "Model.h"
2
3 Model::Model(int d):d_(d) {};
4
5 void Model::setData(const int d)
6 {
7 d_=d;
8 notify();
9 }
10
11 int Model::getData()const
12 {
13 return d_;
14 }
15
16 void Model::attach(Observer*s)
17 {
18 registry.insert(s);
19 }
20
21 void Model::detach(Observer*s)
22 {
23 registry.erase(s);
24 }
25
26 void Model::notify()
27 {
28 std::set<Observer*>::iterator it;
29 for (it=registry.begin(); it!=registry.end(); it++)
30 (*it)->update();
31 }
32
33




Обобщенное представление (View)



1 #ifndef VIEW_H
2 #define VIEW_H 1
3 class Controller;
4 #include "Observer.h"
5 #include "Model.h"
6 #include "Controller.h"
7 class View: public Observer
8 {
9 protected:
10 Model *myModel;
11 Controller *myController;
12 public:
13 View(Model *m);
14 virtual ~View();
15 virtual void update();
16 virtual void draw();
17 virtual void initialize();
18 virtual Controller* makeController();
19 Model* getModel();
20 Controller *getController();
21 public:
22 virtual void setConroller(Controller *ctrl);
23 };
24 #endif




Определения методов класса View



1 #include "View.h"
2
3 View::View(Model *m):myModel(m),myController(0)
4 {
5 myModel->attach(this);
6 }
7
8 View::~View()
9 {
10 myModel->detach(this);
11 }
12
13 void View::initialize()
14 {
15 myController=makeController();
16 }
17
18 Controller* View::makeController()
19 {
20 return new Controller(this);
21 }
22
23 Model* View::getModel()
24 {
25 return myModel;
26 }
27
28 Controller * View::getController()
29 {
30 return myController;
31 }
32
33 void View::update()
34 {
35 this->draw();
36 }
37
38 void View::draw() {}
39
40 void View::setConroller(Controller *ctrl)
41 {
42 myController=ctrl;
43 }




Контроллер (Controller)



1 #ifndef CONTROLLER_H
2 #define CONTROLLER_H 1
3 #include <wx/event.h>
4 class View;
5 #include "Observer.h"
6 #include "View.h"
7 #include "Model.h"
8 typedef wxCommandEvent Event;
9 class Controller : public Observer
10 {
11 public:
12 virtual void handleEvent(Event *);// default = no op
13
14 Controller(View* v) ;
15 Controller() ;
16 virtual ~Controller();
17 virtual void update(); // default = no op
18 protected:
19 Model *myModel;
20 View *myView;
21 };
22 #endif




Определения методов контроллера



1 #include "Controller.h"
2 void Controller::handleEvent(Event *) {}
3 Controller::Controller(View* v) : myView(v)
4 {
5 myModel = myView->getModel();
6 myModel->attach(this);
7 }
8
9 Controller::~Controller()
10 {
11 myModel->detach(this);
12 }
13
14 void Controller::update() {}




Описание класса представления TextViewVal, реализующего отображение данных на основе wxStaticText



1 #ifndef TEXTVIEWVAL_H
2 #define TEXTVIEWVAL_H 1
3 #include <wx/stattext.h>
4 #include "View.h"
5 #include "TextControllerVal.h"
6 class TextViewVal:
public View ,public wxStaticText
7 {
8 public:
9 TextViewVal(wxWindow *parent,wxWindowID id,Model *m, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize,
long style=0, const wxString &name=wxStaticTextNameStr);
10 ~TextViewVal() ;
11 virtual void draw();
12 virtual Controller *makeController();//+
13 };
14 #endif




Определения методов класса TextViewVal



1 #include "TextViewVal.h"
2 TextViewVal::TextViewVal(wxWindow *parent,wxWindowID id,Model *m, const wxPoint &pos, const wxSize &size,
long style, const wxString &name):View(m),wxStaticText(parent,id, wxString::Format(wxT("%d"),m->getData()), pos,size,style,
name) {}
3
4 TextViewVal::~TextViewVal() {}
5
6 void TextViewVal::draw()
7 {
8 SetLabel(wxString::Format(wxT("%d"),myModel->getData()));
9 }
10
11 Controller* TextViewVal::makeController()
12 {
13 return new TextControllerVal(this->GetParent(), -1,this);
14 }




Контроллер для связи Модели с Отображением



1 #ifndef TEXTCONTROLLERVAL_H
2 #define TEXTCONTROLLERVAL_H 1
3 #include <wx/textctrl.h>
4 #include "Controller.h"
5 #include "TextViewVal.h"
6 class TextViewVal;
7 class TextControllerVal : public Controller,public wxTextCtrl
8 {
9 public:
10 TextControllerVal(wxWindow* parent, wxWindowID id,TextViewVal *tv, const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = 0, const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxTextCtrlNameStr) ;
11 virtual void handleEvent(Event *e);
12 };
13 #endif




Определения методов класса TextControllerVal



1 #include "TextControllerVal.h"
2 TextControllerVal::TextControllerVal(wxWindow* parent, wxWindowID id,TextViewVal *tv, const wxPoint& pos,
const wxSize& size , long style, const wxValidator& validator, const wxString& name ):Controller((View*)tv),wxTextCtrl(parent,
id, wxString::Format(wxT("%d"),tv->getModel()->getData()), pos, size, style , validator , name){}
3
4 void TextControllerVal::handleEvent(Event *e)
5 {
6 myModel->setData(wxAtoi(e->GetString().c_str()));
7 }




Описание класса диалога для размещения Представлений и Контроллера



1 #ifndef TEXTDLGVAL_H
2 #define TEXTDLGVAL_H 1
3 #include <wx/dialog.h>
4 #include "TextViewVal.h"
5 class TextDlgVal : public wxDialog
6 {
7 public:
8 TextDlgVal(wxDialog* parent,wxString& title,Model* m);
9 virtual ~TextDlgVal();
10 protected:
11 enum
12 {
13 ID_TXTVAL=1000
14 };
15
16 Model* model_;
17 TextViewVal * view_;
18 TextViewVal * view2_;
19 TextControllerVal *controller_;
20
21 private:
22 void OnClose(wxCloseEvent &event);
23 void OnText(wxCommandEvent &event);
24
25 DECLARE_EVENT_TABLE()
26 };
27 #endif




Определения класса диалога для размещения Контроллера и двух экземпляров класса Представления



1 #include "TextDlgVal.h"
2 BEGIN_EVENT_TABLE(TextDlgVal, wxDialog)
3 EVT_CLOSE(TextDlgVal::OnClose)
4 EVT_TEXT(ID_TXTVAL, TextDlgVal::OnText)
5 END_EVENT_TABLE()
6
7 TextDlgVal::TextDlgVal(wxDialog* parent,wxString& title,Model* m):wxDialog(parent,-1,title),model_(m),view_(0),controller_(0)
8 {
9 this->SetSizeHints(wxDefaultSize, wxDefaultSize);
10 wxBoxSizer* bSizer1;
11
12 bSizer1 = new wxBoxSizer(wxVERTICAL);
13
14 view_=new TextViewVal(this,-1,m);
15 view2_=new TextViewVal(this,-1,m);
16
17 view_->initialize();//Инициализируем дл� нашего пред�тавлени� контроллер
18 controller_=(TextControllerVal*) view_->getController();//
19 controller_->SetId(ID_TXTVAL);//Делаем возможным обработку �обытий от контроллера
20
21 //Св�жем пред�тавление view2_ � контроллером
22 view2_->setConroller(controller_);
23
24 bSizer1->Add(view_, 0, wxALL|wxEXPAND, 5);
25 bSizer1->Add(view2_, 0, wxALL|wxEXPAND, 5);
26
27 bSizer1->Add(controller_, 1, wxEXPAND, 5);
28
29 this->SetSizer(bSizer1);
30 this->Layout();
31 bSizer1->Fit(this);
32 }
33
34 TextDlgVal::~TextDlgVal(){}
35
36 void TextDlgVal::OnClose(wxCloseEvent &event)
37 {
38 Destroy();
39 }
40
41 void TextDlgVal::OnText(wxCommandEvent &event)
42 {
43 controller_->handleEvent(&event);
44 }
45




Класс приложения wxWidgets



1 #ifndef MVC_TAPP_H
2 #define MVC_TAPP_H
3 #include <wx/app.h>
4 class MVC_TApp : public wxApp
5 {
6 public:
7 virtual bool OnInit();
8 };
9 #endif // MVC_TAPP_H




Определение приложения



1 #ifdef WX_PRECOMP
2 #include "wx_pch.h"
3 #endif
4
5 #ifdef __BORLANDC__
6 #pragma hdrstop
7 #endif //__BORLANDC__
8
9 #include "MVC_TApp.h"
10 #include "TextDlgVal.h"
11
12 IMPLEMENT_APP(MVC_TApp);
13
14 bool MVC_TApp::OnInit()
15 {
16 Model* m_=new Model(10);
17 wxString stro=wxT("MVC TEST");
18
19 TextDlgVal* v1_ = new TextDlgVal(0L,stro,m_);
20 v1_->Show();
21
22 return true;
23 }




Модуль для прекомпиляции (можно и без него обойтись)



1 #ifndef WX_PCH_H_INCLUDED
2 #define WX_PCH_H_INCLUDED
3
4 // basic wxWidgets headers
5 #include <wx/wxprec.h>
6
7 #ifdef __BORLANDC__
8 #pragma hdrstop
9 #endif
10
11 #ifndef WX_PRECOMP
12 #include <wx/wx.h>
13 #endif
14
15 #ifdef WX_PRECOMP
16 // put here all your rarely-changing header files
17 #endif // WX_PRECOMP
18
19 #endif // WX_PCH_H_INCLUDED