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);
воскресенье, 27 ноября 2011 г.
Пример использования SQLite3
В примере используется функция wxLogMessage из библиотеки wxWidgets для вывода на экран, можно ее заменить printf, убрав wxT, суть не в этом, только надо помнить, что SQLite3 библиотека использует Unicode (UTF-8).
среда, 16 ноября 2011 г.
Вирус - вымогатель
На одном из компов при входе под пользователем, появилось сообщение угрожающего содержания.
Что делать?
Уходим в спящий режим. Загружаемся.
Входим под другим пользователем с правами администратора.
Запускаем диспетчер задач.
Включаем галочку "Отображать процессы всех пользователей".
Находим подозрительную задачу нашего неосторожного пользователя и завершаем её.
Меняем пользователя, заходим под проблемным пользователем и запускаем диспетчер задач.
В меню диспетчера задач выбираем Выполнить и набираем "Мои документы" или explorer, чтоб панель задач появилась и другие сомнительные удобства Windows.
Лечим, ...
Что делать?
Уходим в спящий режим. Загружаемся.
Входим под другим пользователем с правами администратора.
Запускаем диспетчер задач.
Включаем галочку "Отображать процессы всех пользователей".
Находим подозрительную задачу нашего неосторожного пользователя и завершаем её.
Меняем пользователя, заходим под проблемным пользователем и запускаем диспетчер задач.
В меню диспетчера задач выбираем Выполнить и набираем "Мои документы" или explorer, чтоб панель задач появилась и другие сомнительные удобства Windows.
Лечим, ...
понедельник, 14 ноября 2011 г.
Создание базы sqlite3 из командного файла
Имею файл db.sql, сожержащий команды для консольного sqlite3-клиента.
В Windows делаю так:
Эти команды можно прописать в командном файле для cmd.
В ОС семейства Unix можно выполнить:
Более универсально, делать так
В db.sql можно запихнуть, в принципе, достаточно сложные команды, да и сам командный файл можно сделать не тривиалным, если знать команды оболочки и SQL.
В 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 относится к строке заголовка окна. Такая организация расширяет возможности повторного использования кода, не говоря уж об удобстве локализации.
Давайте взглянем на класс WinClass. Он инкапсулирует вызов Windows-определенной структуры WNDCLASSEX и обеспечивает приемлемые значения по умолчанию для всех его полей. Этот класс является производным от простого класса WinSimpleClass, который вы можете использовать для встраивания некоторых Windows классов (подобных кнопкам, спискам и т.д.).
Я определил, примерные методы, которые могут быть использованы для переопределения значений по умолчанию. Например, SetBgSysColor изменяет цвет фона определенный по умолчанию в клиентской области окна в один из предопределенных системой цвет. Метод SetResIcons загружает соответствующие иконки из ресурсов и закрепляет их за оконным классом. Эти иконки будут потом появляться в верхнем левом углу главного окна и на панели задач Windows.
TopWinClass производный от WinClass позволяет использовать его методы. Он также устанавливает меню вверху оконного класса.
Од;нажды зарегистрировав в системе Windows класс, вы можете создать много окон этого класса, если только захотите. Они, конечно, будут совместно использовать некоторую оконную процедуру, которая была зарегистрированная вместе с классом. Мы увидим позже, как можно различать экземпляры окна внутри процедуры.
Класс WinMaker работает в большинстве случаев как WinClass. Его конструктор обеспечивает разумные значения по умолчанию, которые могут быть переопределены посредством вызова специальных методов. Как только все определено, вы вызываете метод Create, который создает окно и метод Show для его отображения. Внимание, в тот момент, когда вы вызываете Create, ваша оконная процедура вызывается с сообщением WM_CREATE .
Окно верхнего уровня, созданное с использованием класса TopWinMaker, который обеспечивает соответствующий стиль и надпись.
Перед тем как мы пойдем дальше, здесь будут некоторые легкие классы для общей выгоды. WinException - это примерно, то, что мы хотим, он выбросит исключение в любом случае при неудаче с Windows API. Он позаботится о возврате кода ошибки Windows. Для конвертации кода ошибки в строку используется FormatMessage API.
Класс ResString является простой l0;нкапсуляцией хранения строковых ресурсов вашего приложения.
Contriller (контроллер) - это нервная система исключительно оконного экземпляра. Он создается вместе с окном, сохраняется с ним и разрушается с ним. Вы можете определить любую информацию характерную для экземпляра окна в его контроллере. В общем, контроллер связан с представлением (view), которое отрисовывает на поверхности окна и он имеет доступ к модели (model), которая является мозгом вашего приложения (это называют MVC, или Model-View-Controller паттерн, изобретенный Smalltalk программистами).
Как это часто бывает, ваше приложение имеет только одно окно верхнего уровня, вы можете непосредственно включить модель (model) в его контроллер (controller). Это упрощает управление ресурсами, но там возникают накладные расходы на соединение контроллера с моделью. В крупных проектах избегают таких соединений — используют (умный) указатель на модель внутри контроллера, что предпочтительно.
Большинство методов контроллера зависят от дескриптора окна, которым они оперируют. Это дескриптор передается с каждым сообщением Windows, но его проще хранить один раз внутри объекта контроллера и использовать его всякий раз когда необходимо. Помните, они находятся в отношении один-к-одному, соответственно между окном экземпляра (и следовательно дескрипторами окна) и объектами контроллера.
Оконная процедура - это главный коммутатор приложения Windows. Вы не вызываете ее из; вашей программы - Windows делает это! Кажды;й раз что-то интересное происходит, Windows отправляет вашей программе сообщения. Это сообщение передается в вашу оконную процедуру. Вы можете обработать его, или вы можете передать его оконной процедуре по умолчанию.
Оконная процедура вызывается с дескриптором окна, которому переданное сообщение направлено. Этот дескриптор однозначно идентифицирует внутреннюю структуру данных Windows, которая соответствует переданному экземпляру окна. К стати, GWL_USERDATA часть этой структуры гарантированно представлена во всех окнах, включая окна сообщений (MessageBox), диалоги и кнопки.
Каждый раз Windows вызывает нашу оконную процедуру, мы должны вначале определить объект-контроллер. Помните, что может быть несколько окон разделяющих некоторую оконную процедуру, и мы желаем иметь отдельный контроллер для каждого окна. Как можем мы узнать, какой контроллер использовать, когда мы делаем вызов? Мы находим его посредством дескриптора окна. В этом дескрипторе мы храним указатель на контроллер окна, используем Win[Set/Get]Long функций.
При первом вызове, оконная процедура вызывается с WM_CREATE сообщением. В этот время мы создаем объект контроллер и инициализируем его дескриптором окна и специальной структурой данных называемой CREATESTRUCT, которую мы передаём в распор;яжение Windows. Как только мы имеем контроллер, мы сохраняем указатель на него в соответствующую внутреннюю структуру данных Windows под меткой текущего hwnd (дескриптор окна). В следующий раз оконная процедура вызывается с сообщением отличным от WM_CREATE, мы просто находим указатель на наш контроллер, используя hwnd.
Спокойно отдохнем. Оконная процедура интерпретирует параметры сообщения и вызывает соответствующие методы контроллера.
Здесь примеры простого выполнения нескольких методов контроллера. Конструктор запоминает дескриптор окна для дальнейшего использования, деструктор отправляет сообщение о выходе. Метод Size передает его аргумент в View, и т. д. Мы говорим об отрисовке окна немного позже. Теперь, заметим, что контроллер подготавливает область рисования для работы View.
Когда пользователь выбирает один из пунктов меню, оконная процедура вызывается с сообщением WM_COMMAND. Соответствующий метод контроллера отправляет команду, зависящую от идентификатора (id). Когда вы создаете меню используя ваш редактор ресурсов, вы выбираете эти командные идентификаторы для каждого пункта меню. Они хранятся в соответствующем заголовочном файле (resource.h в нашем случае), он должен быть включен в исходник контроллера.
Наше меню содержит только три пункта с идентификаторами IDM_EXIT, IDM_HELP, и IDM_ABOUT. Диалог, который отображается в ответ на IDM_ABOUT, также создан с использованием редактора ресурсов, и ему дан идентификатор IDD_ABOUT. Его диалоговая процедура - AboutDlgProc.
Наконец, для нормального отображения диалога мы нуждаемся в дескрипторе экземпляра приложения. Обычно, доступ к нему получаn2;т через внутреннюю структуру данных Windows, использованием соответствующего hwnd.
Объект представления обычно хранит параметры клиентской области. Они обновляются всякий раз, когда процессы контроллера сообщают WM_SIZE. Первое WM_SIZE сообщение посылается вовремя создания и перед WM_PAINT, так мы можем быть уверены, что к моменту вызова Paint, параметры клиентской области известны.
Графический вывод в окно сделан, посредством вызова соответствующих методов объекта Canvas. В нашем случае, мы отрисовываем текст получаемый из модели и рисуем вертикальную линию на 10 пикселей левее от края клиентской области.
Объект холста инкапсулирует, то что называется контекстом устройства (Device Context) в языке Windows. Наш Canvas - это очень прост, он только знает как печатать текст и рисовать линии, но ваш Canvas может иметь на много больше методов, которые делают творческие вещи.
Холст (canvas), что вы создаете, в ответ на сообщение WM_PAINT может быть специально изменен. Этот контекст устройства захватывается вызовом BeginPaint и освобождается вызовом EndPaint. PAINTSTRUCT содержит дополнительную информацию о каждой части клиентской области, которая отрисована и др. Здесь мы игнорируем некоторые детали, но если это важно для вас вы могли бы узнать больше об этом.
Хотели бы вы программировать без контролов?
Эта программа использует основной набор классов инкапсулирующих 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;
};
Хотели бы вы программировать без контролов?
Подписаться на:
Сообщения (Atom)