Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Диалог в классе  (Прочитано 18769 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Peacedeath
Интересующийся

ru
Offline Offline

« : 29-03-2011 12:54 » 

Объясните начинающему... Хочу создать класс, в котором будет диалог созданный с помощью CreateDialog. Функции класса будут функции работы с полем РичЕдит в этом диалоге, а также приватные поля...
(Собственно кусок код)
Код: (C++)
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)&param, (LPARAM)st));
        return 0;
}

А теперь вопросы:
1. Как создать класс не используя статическую CALLBACK функцию диалога, что бы можно было создавать несколько экземпляров данного класса?
2. Если я создаю статическую CALLBACK функцию диалогового окна в классе (иначе если CALLBACK функция мембер - CreateDialog ругается "приведение типов: невозможно преобразовать 'overloaded-function' в 'DLGPROC'"), диалог создается, но при попытке обратиться из CALLBACK функции к другому члену класса, ругается "MyClass::ChooseColorA: недопустимый вызов нестатической функции-члена". Почему так и как это решить?

P.S. Выслушаю любую критику и советы, но хотелось бы получить ответы на свои вопросы.  Эврика!
« Последнее редактирование: 29-03-2011 13:41 от Джон » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 29-03-2011 13:26 » 

Peacedeath, а чем тебя не устраивает статическая функция? В ней нет ничего специфического для экземпляра класса, за исключением ChooseColor.

В WinAPI передать метод объекта нельзя. Ведь для вызова метода внутри WinAPI должен быть известен сам объект, метод которого вызывают. А эта информация не передаётся вместе с указателем на функцию-член.

Поэтому тут два пути:

1) Если получится, накрутить при помощи шаблонов или чего-нибудь в этом духе упаковку объекта с методом в функцию - т.е. создать функтор. Но не функтор в смысле STL - там всё же при помощи шаблонов модифицируются алгоритмы, если я правильно это понимаю.

2) Завести статическое поле типа map, в котором хранить пары: ключ - HWND hDlg и значение - указатель на объект. Тогда твой статический метод по hDlg всегда сможет достать нужный объект и вызвать его метод.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Peacedeath
Интересующийся

ru
Offline Offline

« Ответ #2 : 30-03-2011 05:44 » 

А если я захочу создать несколько экземляров этого класса, и в кажом будет окно со своей процедурой обработки событий... где то я видел, что со статическими функциями класса такое не прокатит.... Или я не прав?
Записан
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 30-03-2011 07:51 » 

Peacedeath, если экземпляр класса, с которым работает статическая функция, каждый раз разный, то не вижу никаких проблем. Не прокатит, только если у тебя и сам экземпляр один и статический (singleton, то есть).
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #4 : 30-03-2011 08:55 » 

Peacedeath, конечно, ты можешь написать несколько классов типа того, который ты привёл - если количество экземпляров фиксировано и в runtime не меняется. Такие вещи удобно делать через шаблоны, чтобы не делать copy-paste кода.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Peacedeath
Интересующийся

ru
Offline 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
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 30-03-2011 14:54 » 

Peacedeath, ты объявляешь эти члены private. Такие члены закрыты для внешнего доступа. Когда ты передаёшь свой callback внутрь WinAPI, то WinAPI по отношению к твоему классу, естественно, находится "снаружи" и поэтому не может "видеть" private полей.

Насколько я знаю, нет литературы по написанию таких классов. Поскольку на WinAPI пишет подавляющее меньшинство программистов, а задача у тебя специфическая. Фактически ты пытаешься сделать то, что уже давно реализовано в любой объектной библиотеке-обёртке над WinAPI - книжек по устройству библиотек не пишут (хотя бы и из соображений контроля технологий), пишут руководства по их использованию.
« Последнее редактирование: 30-03-2011 14:56 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Peacedeath
Интересующийся

ru
Offline Offline

« Ответ #7 : 01-04-2011 09:21 » 

Перенес вообще все в public. Ничего не изменилось, такая же ошибка, только вместо private - public.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #8 : 01-04-2011 10:22 » 

а вот так

Код:
class MyClass
{
      static  HWND hwndWin;
};

HWND MyClass::hwndWin=0;

...
...
Записан

Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #9 : 01-04-2011 11:33 » 

Алексей1153++, а смысл? Если потребуется несколько экземпляров?

Я считаю, полезно будет посмотреть, как это в WTL/ATL/MFC сделано, и не изобретать велосипед.
Записан
Peacedeath
Интересующийся

ru
Offline Offline

« Ответ #10 : 01-04-2011 12:22 » 

Алексей1153++, а смысл? Если потребуется несколько экземпляров?

да, именно несколько  экземляров.
а как глянуть это в WTL/ATL/MFC ?
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline 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 » Записан

Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #12 : 02-04-2011 08:45 » 

Peacedeath, исходники ATL, на котором нынче построены и WTL, и MFC, доступны в Visual Studio (хотя бы потому, что там шаблоны). Можно изучать. Смотреть надо где-то в районе CDialog или CDialogImpl - точно не помню, где там корни всего.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #13 : 02-04-2011 16:28 » 

Вад, скорее в районе CWnd
Записан

Chaa
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #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 » Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #15 : 04-04-2011 08:59 » 

Chaa, крутой способ, спасибо Улыбаюсь
Записан

Chaa
Помогающий

ru
Offline Offline
Пол: Мужской

« Ответ #16 : 04-04-2011 09:18 » 

Немного исправил код. Дело в том, что некоторые сообщения, вроде WM_NCCREATE, будут переданы в StaticWndProc еще до вызова SetWindowLongPtr, из CreateDialog.
Видимо из-за этого в ATL используется более сложная схема для связывания хэндла окна и указателя на класс.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #17 : 04-04-2011 09:28 » 

Chaa, ага, то есть ограничения начались, не всё так радужно Улыбаюсь Да и список объектов тоже не помониторишь особо
Записан

Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines