Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Программирование и базы данных (http://forum.oszone.net/forumdisplay.php?f=21)
-   -   Как правильно "запихнуть" оконную процедуру в самодельный класс? (http://forum.oszone.net/showthread.php?t=167300)

crashtuak 14-02-2010 10:03 1346700

Как правильно "запихнуть" оконную процедуру в самодельный класс?
 
Как правильно "запихнуть" оконную процедуру в самодельный класс, и как потом правильно прикрутить ту процедуру к нужному окну(в данном случае-к кнопке)? Вот собственно код класса, прошу строго не судить, мой первый класс в жизни:)(который без оконной процедуры работает нормально):
Код:

class PNG_Button
{
public:
        HWND bt_hwnd;
        HWND bt_parent_hwnd;
        int x_position;
        int y_position;
        int x_size;
        int y_size;
        LPCSTR normal_image;
        LPCSTR focused_image;
        LPCSTR pushed_image;
        LPCSTR draw_image;
        BOOL g_bMouseTrack;
        WNDPROC        btnOld;
        //вот она-процедура
        LRESULT CALLBACK ButtonSubclassProc ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
        {
        switch ( msg )
        {
                case WM_MOUSELEAVE:
                        {
                                g_bMouseTrack = FALSE;
                                draw_image=normal_image;
                                InvalidateRect(bt_parent_hwnd,0,true);
                        }
                        return 0;
                case WM_MOUSEHOVER:
                        {
                                draw_image=focused_image;
                                InvalidateRect(bt_parent_hwnd,0,true);
                        }
                        return 0;
                case WM_LBUTTONDOWN:
                        {
                                draw_image=pushed_image;
                                InvalidateRect(bt_parent_hwnd,0,true);
                        }
                        return 0;
                case WM_LBUTTONUP:
                        {
                                draw_image=focused_image;
                                InvalidateRect(bt_parent_hwnd,0,true);
                        }
                        return 0;

                case WM_MOUSEMOVE:
                        if(g_bMouseTrack==FALSE)
                        {
                                TrackMouse(bt_hwnd);
                                g_bMouseTrack = TRUE;
                        }

                        return 0;
        }
        return CallWindowProc(btnOld, hwnd, msg, wParam, lParam);
        }
        //конец оконной процедуры
       
        //конструктор
        PNG_Button(LPCSTR bt_text,int x_pos,int y_pos,int x_sz,int y_sz,
                HWND bt_parent,
                LPCSTR n_image,
                LPCSTR f_image,
                LPCSTR p_image)
        {
                x_position=x_pos, y_position=y_pos, x_size=x_sz ,y_size=y_sz;
                draw_image=n_image, normal_image=n_image, focused_image=f_image, pushed_image=p_image;
                bt_parent_hwnd=bt_parent;
                g_bMouseTrack = FALSE;
                bt_hwnd=CreateWindowEx(NULL,"Button",bt_text,WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, x_pos, y_pos, x_sz, y_sz,bt_parent, NULL,NULL,NULL);
                //на слудующую строчку очень сильно ругается компилятор
                btnOld = (WNDPROC)SetWindowLong(bt_hwnd, GWL_WNDPROC, (LONG)ButtonSubclassProc);
                TrackMouse(bt_hwnd);
                return 1;
        }
        //конец конструктора
        HWND Get_BT_HWND()
        {
                return bt_hwnd;
        }
        int DrawBTN()
        {
                HDC hdc;
                PAINTSTRUCT  ps;
                hdc = BeginPaint(bt_hwnd, &ps);
                CxImage image;
                image.Load(draw_image,CXIMAGE_FORMAT_PNG);       
                image.Draw(hdc,0,0,-1,-1,0,true);
                EndPaint(bt_hwnd, &ps);
                return 1;
        }

};


ganselo 14-02-2010 17:42 1346952

Цитата:

Цитата crashtuak
Как правильно "запихнуть" оконную процедуру в самодельный класс »

Как вариант сделать её френдом класса. А можно попробовать сделать её статической:
Код:

static LRESULT CALLBACK ButtonSubclassProc ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
Цитата:

Цитата crashtuak
и как потом правильно прикрутить ту процедуру к нужному окну »

В конструкторе например:
Код:

PNG_Button()
{
      //не помню полей структуры btnOld, но примерно так
      btnOld.proc = ButtonSubclassProc;
}


crashtuak 19-02-2010 15:38 1351184

ganselo, если делать статической, то пропадает доступ к членам класа. Можно подробнее про френд класа? Я вроде бы читал, что френдом класа может быть только другой клас...

ganselo 19-02-2010 16:13 1351207

Вот простенький пример:
Код:

class test
{
  public:
        testint _a, int _b) : a(_a), b(_b) {}
  private:
        int a, b;
  friend int summa(const test &t);
};

int summa(const test &t)
{
    return t->a + t->b;
}

int main()
{
    test t(10, 20);
    cout <<  summa(t);
    return 0;
}

Что такое функция френд? Эта функция которая не является методом класса, но при этом имеет доступ к закрытым методам класса.

crashtuak 19-02-2010 16:41 1351225

ganselo, хм, интересно. Но так не пойдет, потому что функция-френд будет оконной процедурой, для которой добавить еще один аргумент для передачи указателя на наш клас не есть возможным. Надо как то запихнуть процедуру непосредствено в клас...

pva 23-02-2010 00:09 1353655

передача указателя решается несколькими путями:
1. через отображение HWND -> my_class* (самый защищённый)
где-то хранится массив пар HWND, my_class*; функция WindowProc ищет в этом массиве свой HWND и для найденного(ных) указателя(лей) выполняет какой-нить виртуальный my_class::__wndproc
2. установка SetWindowLong (ИМХО самый экономный)
для субклассинга лучше использовать GWL_USERDATA, хотя можно и зарегистировать новый класс на основании старого, только увеличить cbHwndExtra (или как его там), и хранить в нём нужную информацию
3. через свойства SetProps (ну тоже хороший способ)
главное угадать такое название свойства, которое ещё никто не придумал. Прикрутить к этому названию свой указатель. Я замерял производительность - после 4 способа - самый быстрый, название свойства брал reinterpret_cast<wchar_t*>(1).
4. через сгенерированную в памяти функцию (самый быстроработающий)
для каждого окна в памяти выделяется блок 32 байта, в который пишется машинный код
Код:

mov eax, указатель_на_класс
mov[esp+4], eax
jmp WindowProc
nop
nop
nop

Окну в качестве процедуры выставляется адрес этих 32 байт
теперь у оконной процедуры вместо HWND будет указатель_на_класс. Этот способ используется во многих библиотеках. Недостаток: для правильной работы требуется правильно расположить в памяти. Например если эти 32 байта взять внутри my_class, оно может не работать (у меня не стало, а то же самое, во вновь выделенной памяти - на ура)


Время: 09:43.

Время: 09:43.
© OSzone.net 2001-