Peacedeath
Интересующийся
Offline
|
|
« : 29-03-2011 12:54 » |
|
Объясните начинающему... Хочу создать класс, в котором будет диалог созданный с помощью CreateDialog. Функции класса будут функции работы с полем РичЕдит в этом диалоге, а также приватные поля... (Собственно кусок код) class MyClass { HWND hwndWin; HINSTANCE hInst; BOOL CALLBACK GoToProc(HWND, UINT, WPARAM, LPARAM); int ChooseColor();
public: MyClass(HINSTANCE); ~MyClass(); int Put(char *); };
MyClass::MyClass(HINSTANCE hInstance) { hInst = hInstance; char lpszBuf[80];
if (!IsWindow(hwndWin)) { hwndWin = CreateDialog( hInst, "IDD_DLG", NULL, (DLGPROC)GoToProc ); if (!hwndWin) { FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(),LANG_SYSTEM_DEFAULT,lpszBuf,80,NULL ); MessageBox( NULL, lpszBuf, "Ошибка", MB_OK | MB_ICONWARNING ); } } };
MyClass::~MyClass() { DestroyWindow(hwndWin); hwndWin = NULL; };
BOOL CALLBACK MyClass::GoToProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: { ShowWindow( hDlg, SW_SHOW ); return TRUE; } case WM_COMMAND: switch ( LOWORD(wParam) ) { case IDCANCEL: ShowWindow( hDlg, FALSE ); return FALSE; case IDB_TLIGHT: ChooseColor(); return FALSE; } return FALSE; } return FALSE; };
int MyClass::ChooseColor() {return 0};
int MyClass::Put(char *st) { SETTEXTEX param; param.flags = ST_DEFAULT | ST_SELECTION; param.codepage = CP_ACP; if (!SendMessage(GetDlgItem(hwndWin, IDC_REDIT1), EM_SETTEXTEX, (WPARAM)¶m, (LPARAM)st)); return 0; } А теперь вопросы: 1. Как создать класс не используя статическую CALLBACK функцию диалога, что бы можно было создавать несколько экземпляров данного класса? 2. Если я создаю статическую CALLBACK функцию диалогового окна в классе (иначе если CALLBACK функция мембер - CreateDialog ругается " приведение типов: невозможно преобразовать 'overloaded-function' в 'DLGPROC'"), диалог создается, но при попытке обратиться из CALLBACK функции к другому члену класса, ругается " MyClass::ChooseColorA: недопустимый вызов нестатической функции-члена". Почему так и как это решить? P.S. Выслушаю любую критику и советы, но хотелось бы получить ответы на свои вопросы.
|
|
« Последнее редактирование: 29-03-2011 13:41 от Джон »
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #1 : 29-03-2011 13:26 » |
|
Peacedeath, а чем тебя не устраивает статическая функция? В ней нет ничего специфического для экземпляра класса, за исключением ChooseColor.
В WinAPI передать метод объекта нельзя. Ведь для вызова метода внутри WinAPI должен быть известен сам объект, метод которого вызывают. А эта информация не передаётся вместе с указателем на функцию-член.
Поэтому тут два пути:
1) Если получится, накрутить при помощи шаблонов или чего-нибудь в этом духе упаковку объекта с методом в функцию - т.е. создать функтор. Но не функтор в смысле STL - там всё же при помощи шаблонов модифицируются алгоритмы, если я правильно это понимаю.
2) Завести статическое поле типа map, в котором хранить пары: ключ - HWND hDlg и значение - указатель на объект. Тогда твой статический метод по hDlg всегда сможет достать нужный объект и вызвать его метод.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Peacedeath
Интересующийся
Offline
|
|
« Ответ #2 : 30-03-2011 05:44 » |
|
А если я захочу создать несколько экземляров этого класса, и в кажом будет окно со своей процедурой обработки событий... где то я видел, что со статическими функциями класса такое не прокатит.... Или я не прав?
|
|
|
Записан
|
|
|
|
Вад
|
|
« Ответ #3 : 30-03-2011 07:51 » |
|
Peacedeath, если экземпляр класса, с которым работает статическая функция, каждый раз разный, то не вижу никаких проблем. Не прокатит, только если у тебя и сам экземпляр один и статический (singleton, то есть).
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #4 : 30-03-2011 08:55 » |
|
Peacedeath, конечно, ты можешь написать несколько классов типа того, который ты привёл - если количество экземпляров фиксировано и в runtime не меняется. Такие вещи удобно делать через шаблоны, чтобы не делать copy-paste кода.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Peacedeath
Интересующийся
Offline
|
|
« Ответ #5 : 30-03-2011 10:12 » |
|
Когда я сделал функции статическими, появились сообщения "недопустимая ссылка на нестатический член 'MyClass::hwndWin'". Когда я сделал статическими HWND hwndWin и HINSTANCE hInst, появились ошибки "неразрешенный внешний символ ""private: static struct HWND__ * MyClass::hwndWin" (?hwndWin@MyClass@@0PAUHWND__@@A)"" и такая же для hInst. Что я делаю не так? Может есть какая то литература по написанию таких классов??
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #6 : 30-03-2011 14:54 » |
|
Peacedeath, ты объявляешь эти члены private. Такие члены закрыты для внешнего доступа. Когда ты передаёшь свой callback внутрь WinAPI, то WinAPI по отношению к твоему классу, естественно, находится "снаружи" и поэтому не может "видеть" private полей.
Насколько я знаю, нет литературы по написанию таких классов. Поскольку на WinAPI пишет подавляющее меньшинство программистов, а задача у тебя специфическая. Фактически ты пытаешься сделать то, что уже давно реализовано в любой объектной библиотеке-обёртке над WinAPI - книжек по устройству библиотек не пишут (хотя бы и из соображений контроля технологий), пишут руководства по их использованию.
|
|
« Последнее редактирование: 30-03-2011 14:56 от Dimka »
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
Peacedeath
Интересующийся
Offline
|
|
« Ответ #7 : 01-04-2011 09:21 » |
|
Перенес вообще все в public. Ничего не изменилось, такая же ошибка, только вместо private - public.
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #8 : 01-04-2011 10:22 » |
|
а вот так class MyClass { static HWND hwndWin; };
HWND MyClass::hwndWin=0;
... ...
|
|
|
Записан
|
|
|
|
Вад
|
|
« Ответ #9 : 01-04-2011 11:33 » |
|
Алексей1153++, а смысл? Если потребуется несколько экземпляров?
Я считаю, полезно будет посмотреть, как это в WTL/ATL/MFC сделано, и не изобретать велосипед.
|
|
|
Записан
|
|
|
|
Peacedeath
Интересующийся
Offline
|
|
« Ответ #10 : 01-04-2011 12:22 » |
|
Алексей1153++, а смысл? Если потребуется несколько экземпляров?
да, именно несколько экземляров. а как глянуть это в WTL/ATL/MFC ?
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #11 : 01-04-2011 16:03 » |
|
Вад, я только ответил на часть с "неразрешенный внешний символ"
а про мап уже Димка сказал
Добавлено через 5 минут и 24 секунды: Вад, как это в WTL/ATL/MFC сделано - понятия не имею, но лично я делал через статический мап (вернее - два, взаимодополняющих)
std::map<HWND, WndPROC> m_HWND_PROC; std::map<WndPROC, HWND> m_PROC_HWND;
изменяются строго одновременно. Если есть многопоточность - ввести синхронизацию
|
|
« Последнее редактирование: 01-04-2011 16:08 от Алексей1153 »
|
Записан
|
|
|
|
Вад
|
|
« Ответ #12 : 02-04-2011 08:45 » |
|
Peacedeath, исходники ATL, на котором нынче построены и WTL, и MFC, доступны в Visual Studio (хотя бы потому, что там шаблоны). Можно изучать. Смотреть надо где-то в районе CDialog или CDialogImpl - точно не помню, где там корни всего.
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #13 : 02-04-2011 16:28 » |
|
Вад, скорее в районе CWnd
|
|
|
Записан
|
|
|
|
Chaa
|
|
« Ответ #14 : 04-04-2011 08:29 » |
|
В ATL используется довольно сложная система для получения из хэндла окна указателя на класс. Корни всего находятся в CWindow и CWndProcThunk. Если не использовать ATL, то лучше сделать так: class MyClass { HWND hwndWin;
static BOOL CALLBACK StaticWndProc(HWND, UINT, WPARAM, LPARAM); BOOL CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
public: MyClass(); ~MyClass(); };
MyClass::MyClass() { hwndWin = CreateDialog( hInst, "IDD_DLG", NULL, (DLGPROC)StaticWndProc); SetWindowLongPtr(hwndWin, GWLP_USERDATA, static_cast<LONG_PTR>(this)); };
MyClass::StaticWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { MyClass* pThis = static_cast<MyClass*>(GetWindowLongPtr(hDlg, GWLP_USERDATA)); if (pThis) { return pThis->WndProc(hDlg, message, wParam, lParam); } else { return DefWindowProc(hDlg, message, wParam, lParam); } };
MyClass::WndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { // Можно использовать члены класса }; Общая идея - указатель на экземпляр класса сохраняется в пользовательском атрибуте окна - GWLP_USERDATA. При вызове оконной процедуры с помощью этого указателя вызывается метод WndProc класса.
|
|
« Последнее редактирование: 04-04-2011 09:10 от Chaa »
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #15 : 04-04-2011 08:59 » |
|
Chaa, крутой способ, спасибо
|
|
|
Записан
|
|
|
|
Chaa
|
|
« Ответ #16 : 04-04-2011 09:18 » |
|
Немного исправил код. Дело в том, что некоторые сообщения, вроде WM_NCCREATE, будут переданы в StaticWndProc еще до вызова SetWindowLongPtr, из CreateDialog. Видимо из-за этого в ATL используется более сложная схема для связывания хэндла окна и указателя на класс.
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #17 : 04-04-2011 09:28 » |
|
Chaa, ага, то есть ограничения начались, не всё так радужно Да и список объектов тоже не помониторишь особо
|
|
|
Записан
|
|
|
|
|