среда, 8 сентября 2010 г.

Как создать приложения с диалогом на WinAPI в Visual C++

Создать простое оконное приложение в Visual C++ с использованием только WinAPI, можно с помощью готового мастера следующим образом:
Создать новый проект File -> New Project -> Visual C++ -> Win32 -> Win32 Project.
В появившемся мастере сразу жмем Finish. При этом будут сгенерирован исходный код приложения с одним основным окном и одним диалогом About.
Для того чтобы создать более простое приложение на основе диалога из ресурса (в Visual C++ Professional и выше, т. к. в Express версии нету редактора ресурсов), проще всего создать пустой проект (для этого, при создании проекта, в мастере перейти на вкладку Application Settings и поставить галочку Empty Project) и добавить нужные файлы вручную. В простейшем приложении с одним диалогом достаточно будет добавить один файл main.cpp и файл ресурса. В редакторе ресурсов создаем новый диалог. В созданный файл main.cpp добавляем следующий код:
#include <windows.h>
#include "resource.h"
INT_PTR CALLBACK WndProc(HWND hDlg, UINT message, WPARAM wParam,
LPARAM lParam);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
INT_PTR ret = DialogBoxA(hInstance,
MAKEINTRESOURCEA(IDD_DIALOG_MAIN), NULL, WndProc);
int err = GetLastError();
return 0;
}
INT_PTR CALLBACK WndProc(HWND hDlg, UINT message, WPARAM wParam,
LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
break;
case IDC_BUTTON2:
break;
}
}
return (INT_PTR)FALSE;
}

среда, 1 сентября 2010 г.

Перехват событий Windows

Для перехвата событий в Windows, предусмотрена API-функция SetWindowsHookEx. С помощью нее можно перехватить как события отдельной нити, так и глобальные события Windows. Функция SetWindowsHookExA имеет следующую сигнатуру:
HHOOK WINAPI SetWindowsHookEx(
__in int idHook,
__in HOOKPROC lpfn,
__in HINSTANCE hMod,
__in DWORD dwThreadId
);

С помощью этой API-функции можно перехватывать различные события, в том числе и событие от мыши и клавиатуры. Подробное описание функции лежит в MSDN.

Для того чтобы перехватить событие текущей нити, достаточно определить функцию, соответствующую сигнатуре HOOKPROC и вызвать функцию SetWindowsHookEx, как приведено в примере ниже:
LRESULT CALLBACK hook_proc(int code, WPARAM wParam,
LPARAM lParam)
{
MessageBoxA(NULL, "Key Pressed", "Key Pressed", MB_OK);
return CallNextHookEx(NULL, code, wParam, lParam);
}

//Где-то в программе {
HHOOK hHook = SetWindowsHookExA(WH_KEYBOARD, hook_proc, NULL,
GetCurrentThreadId());
if (hHook == NULL)
{
DWORD err = GetLastError();
char chr[20];
itoa(err, chr, 10);
MessageBoxA(hWnd, chr, "Error", MB_OK | MB_ICONERROR);
}
else
{
MessageBoxA(hWnd, chr, "Keyboard Evend Hooked!",
MB_OK | MB_ICONERROR);
}
//}
Исходный код приведенного примера лежит тут.

Перехват глобального события требует создания DLL с функцией-обработчиком события. Подробно о создании DLL написано здесь.
Исходный код примера можно загрузить по этой ссылке.

понедельник, 30 августа 2010 г.

Как создать DLL с функциями

Для того чтобы создать динамическую библиотеку в Visual Studio, нужно сначала создать проект: File -> New Project -> Visual C++ -> Win32 Console Application, задать имя проекта (например MyLibrary1) и нажать OK. В появившемся диалоге, на вкладке Application Settings выбрать DLL как тип приложения (Application type). Чтобы создать пустой проект, нужно поставить галочку Empty Project. После этого нажать Finish.
Если галочка Empty Project, при создании проекта, не была поставлена, то будут созданы все необходимые файлы.
Если галочка Empty Project была поставлена, то проект будет пустым и в него, вручную, нужно добавить необходимые файлы. Самый простой вариант, это добавить один файл C++ (Project -> Add New Item -> C++ File (.cpp)) с именем проекта (в нашем случае MyLibrary1.cpp). В него поместить следующий код:
#include <windows.h>
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpReserved ) // reserved
{
// Perform actions based on the reason for calling.
switch( fdwReason )
{
case DLL_PROCESS_ATTACH:
// Initialize once for each new process.
// Return FALSE to fail DLL load.
break;
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_PROCESS_DETACH:
// Perform any necessary cleanup.
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}

Функции, которые должны быть видимыми для внешних программ, можно поместить в этом же файле, как написано в примере ниже:
extern "C" {
__declspec(dllexport) int func() {
return 0;
}
}

В приведенном выше примере:
Строка 1: Указывает компилятору, что функция именуется в стиле C (убирается декорирование С++, описывающее аргументы функции).
Строка 2: __declspec(dllexport) указывает компилятору, что функция должна быть видима внешним программам.
Также, будет полезно добавить в проект DEF-файл, содержащий описание экспортируемых функций:
LIBRARY "MyLibrary1"
EXPORTS
func

Скомпилируйте проект. Если сделано все как написано, то проблем с дальнейшим импортом функций из библиотеки не возникнет.

Простейшая реализация оператора new

Самая простая пользовательская реализация оператора new, соответствующая всем требованиям стандарта, может выглядеть так:
#include <new>
#include <cstdlib>
void* operator new(std::size_t size) throw(std::bad_alloc) {
if (size == 0)
size = 1;
while (true) {
void* ret = std::malloc(size);
if (ret != 0)
return ret;
std::new_handler handler = std::set_new_handler(0);
if (handler != 0) {
std::set_new_handler(handler);
(*handler)();
}
else
throw std::bad_alloc();
}
}

Строка 1-2: Включение необходимых файлов.
Строка 3: Оператор new может вызывать только исключение std::bad_alloc.
Строки 4-5: Оператор new должен корректно отрабатывать выделение нулевого количество байт.
Строка 6: Выделение памяти и вызов функции-обработчика, в случае невозможности выделения памяти, должны выполняется внутри бесконечного цикла.
Строка 7: Выделение памяти.
Строки 8-9: Если выделение памяти прошло успешно, оператор new возвращает указатель на нее.
Оператор new в случае невозможности, по какой-либо причине, выделения нужный объем памяти, вызывает функция-обработчик, если она задана или вызывает исключение std::bad_alloc(), если она не задана.
Строка 10: Получение указателя на функцию-обработчик.
Строка 11-13: Если функция-обработчик задана, оператор new вызывает его.
Строка 15-16: Если функция-обработчик не задана, оператор new вызывает исключение std::bad_alloc().
Исходный код примера здесь.