Модераторы: feodorv, GremlinProg, xvr, Fixin
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> RegisterClassEx, Параметр WNDPROC lpfnWndProc 
:(
    Опции темы
uvsoft
  Дата 2.10.2005, 15:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 123
Регистрация: 4.2.2003

Репутация: нет
Всего: нет



hi,

тут такое дело, хочу зарегистрировать класс окна пишу что-т вроде:
Код

WNDCLASSEX wcx; 
// bla-blaing
wcx.lpfnWndProc = MainWindowProc;
// bla-blaing
RegisterClassEx(&wcx);

где MainWindowProc:
LRESULT CALLBACK MainWindowProc(HWND, UINT, WPARAM, LPARAM);

компилирую, все в порядке....

НО, если все это оформить в виде класса, т.е. занести функцию MainWindowProc в класс, то компилятор начинает ругаться:

f:\...\file.cpp(32): error C2440: '=' : cannot convert from 'LRESULT (__stdcall MyNameSpace::CMyClass::* )(HWND,UINT,WPARAM,LPARAM)' to 'WNDPROC'

тут есть какая тонкость в использовании MainWindowProc?

спасибо
--------------------
Старый глюк лучше новых двух.
PM MAIL ICQ   Вверх
srd
Дата 2.10.2005, 18:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Нереварин
**


Профиль
Группа: Участник Клуба
Сообщений: 951
Регистрация: 30.8.2003
Где: Владивосток

Репутация: 4
Всего: 49



Оконная процедура должна быть глобальной функцией. Если она член класса, то должна быть статической.


--------------------
Не смей читать мою подпись!!!
PM MAIL Jabber   Вверх
uvsoft
Дата 3.10.2005, 01:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 123
Регистрация: 4.2.2003

Репутация: нет
Всего: нет



smile спасиб

я так понимаю и все остальные функции обработки сообщений (OnSize, OnCommand) тож должны быть статическими, да?
а от сюда следует, что и все данные, с которыми они работают в классе должны быть статическими, и тут тогда получается что уже никак нельзя использовать два экземпляра класса для разных нужд, так?
--------------------
Старый глюк лучше новых двух.
PM MAIL ICQ   Вверх
srd
Дата 3.10.2005, 05:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Нереварин
**


Профиль
Группа: Участник Клуба
Сообщений: 951
Регистрация: 30.8.2003
Где: Владивосток

Репутация: 4
Всего: 49



Цитата(uvsoft @ 3.10.2005, 09:09)
я так понимаю и все остальные функции обработки сообщений (OnSize, OnCommand) тож должны быть статическими, да?

Вовсе нет. Оконная процедура должна быть статической, потому что вызывается она самой операционной системой, а ОСь использует именно прототип LRESULT (CALLBACK*)(HWND,UINT,WPARAM,LPARAM). Функции-члены же имеют дополнительный параметр, с помощью которой в функцию передаётся указатель this на экземпляр класса. Отсюда и проблемы. Если ты найдёшь способ сохранить связь между дескриптором окна и указателем на экземпляр класса и будешь восстанавливать этот указатель внутри оконной процедуры (существует несколько возможных вариантов), то обработчики сообщений могут быть и обычными функциями-членами.




--------------------
Не смей читать мою подпись!!!
PM MAIL Jabber   Вверх
srd
Дата 3.10.2005, 05:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Нереварин
**


Профиль
Группа: Участник Клуба
Сообщений: 951
Регистрация: 30.8.2003
Где: Владивосток

Репутация: 4
Всего: 49



Простая иллюстрация к вышесказанному:
Код

#include <tchar.h>
#include <windows.h>

class Window
{
public:
    Window( void )
        : itsHandle( NULL )
    {}

    bool create( void );

private:   
    LRESULT handleMessage( UINT, WPARAM, LPARAM );
    static LRESULT CALLBACK wndProc( HWND, UINT, WPARAM, LPARAM );
    static ATOM registerClass( void );
    HWND itsHandle;
};

ATOM Window::registerClass( void )
{
    WNDCLASSEX wc = {0};

    wc.cbSize = sizeof( wc );
    wc.hbrBackground = GetSysColorBrush( COLOR_WINDOW );
    wc.hCursor = LoadCursor( NULL, IDC_ARROW );
    wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
    wc.hInstance = GetModuleHandle( NULL );
    wc.lpfnWndProc = wndProc;
    wc.lpszClassName = _T("WindowCls");
    wc.style = CS_HREDRAW | CS_VREDRAW;

    return RegisterClassEx( &wc );
}

bool Window::create( void )
{
    static ATOM atomClass = registerClass();

    if( atomClass == NULL )
        return false;

    HWND hwnd = CreateWindowEx( WS_EX_OVERLAPPEDWINDOW,
        reinterpret_cast<LPCTSTR>( atomClass ),
        _T("Example"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0,
        CW_USEDEFAULT, 0,
        NULL,
        NULL,
        GetModuleHandle( NULL ),
        this /* пропихиваем в оконную процедуру указатель на экземпляр класса */);

    if( hwnd == NULL || hwnd != itsHandle )
        return false;

    ShowWindow( hwnd, SW_SHOWDEFAULT );
    UpdateWindow( hwnd );

    return true;
}

LRESULT CALLBACK Window::wndProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
{
    const TCHAR* const prop = _T("Window");

    if( msg == WM_NCCREATE )
    {
        /* сохраняем указатель */
        CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>( lparam );
        Window* wnd = static_cast<Window*>( cs->lpCreateParams );
        wnd->itsHandle = hwnd;
        SetProp( hwnd, prop, wnd );
    }

    LRESULT result = 0L;

    if( Window* wnd = static_cast<Window*>( GetProp( hwnd, prop ) ) )
        result = wnd->handleMessage( msg, wparam, lparam );
    else
        result = DefWindowProc( hwnd, msg, wparam, lparam );

    if( msg == WM_NCDESTROY )
        RemoveProp( hwnd, prop );

    return result;
}

LRESULT Window::handleMessage( UINT msg, WPARAM wparam, LPARAM lparam )
{
    // тут можно реализовать карту сообщений или сотворить ещё что-нибудь интересное

    switch( msg )
    {
    case WM_DESTROY:
        PostQuitMessage( EXIT_SUCCESS );
        break;

    default:
        return DefWindowProc( itsHandle, msg, wparam, lparam );
    }

    return 0L;
}

int WINAPI _tWinMain( HINSTANCE, HINSTANCE, LPTSTR, int )
{
    Window wnd;
    if( !wnd.create() )
        return EXIT_FAILURE;

    MSG msg = {0};

    while( GetMessage( &msg, NULL, 0U, 0U ) )
    {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }

    return static_cast<int>( msg.wParam );
}




--------------------
Не смей читать мою подпись!!!
PM MAIL Jabber   Вверх
jonie
Дата 31.7.2006, 15:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 5613
Регистрация: 21.8.2005
Где: Владимир

Репутация: 7
Всего: 118



static это, конечно хорошо... но уж больно хотелось бы получить доступ к членам (в т.ч. объявленных как private) созданного экземпляра класса.....
В общем и целом, я эту проблему решил так:
Код

class SomeWndClass
{
public:SomeWndClass();

private: int some_data;
private:
    HWND MakeWorkerWindow(void);
    friend LRESULT CALLBACK WorkerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};

HWND SomeWndClass::MakeWorkerWindow(void)
{
... стандартно создаем окно...... и возращаем HWND его
}

SomeWndClass::SomeWndClass()
{
 //вызываем создание окна ну и проверяем на ошибки, безусловно
 HWND window;
 if (! (Window=MakeWorkerWindow()) ){
    throw "Апшибка?";
 }

//а вот и ставим оконной процедуре спец данные -- указатель на наш "родной" экземпляр класса
//тожеб нефигова проферить на ошибки 8)
  SetWindowLong(Window,GWL_USERDATA,(DWORD)this);
...........
}

LRESULT CALLBACK WorkerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
 //берем указатель на конкретный экземпляр класса
  SomeWndClass* lpSWClass =(SomeWndClass*)GetWindowLong(hwnd,GWL_USERDATA);
 //обрабатываем сообщения и т.п.
 lpSWClass->some_data = 10;  //должно идейно работать...
...............
}

код написал только что и потому прошу не бить ногами))
комментарии приветсвуются!  

Это сообщение отредактировал(а) jonie - 31.7.2006, 15:41


--------------------
Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет...
PM MAIL Jabber   Вверх
SeregaLBN
Дата 1.8.2006, 11:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 178
Регистрация: 3.10.2003
Где: Киев

Репутация: нет
Всего: нет



Цитата(jonie @  31.7.2006,  15:27 Найти цитируемый пост)
уж больно хотелось бы получить доступ к членам (в т.ч. объявленных как private) созданного экземпляра класса

Есть несколько таких методов:
1. Самый лучшый, имхо, - реализация как в WTL, так называеме thunk'и.
  Почитать можно здесь - http://www.rsdn.ru/article/wtl/wtl-1.xml?print#EQZAE
  Опишу двумя словами. С помощью небольшой асемблерной вставочки, в стеке вызова параметров оконной процедуры заменяется параметр HWND на указатель на свой класс окна, т.е. на this.
  Это самый сложный метод. Для его понимания надо будет дополнительно лезть в ИНет или смотреть исходники WTL.

Остальные попроще:
2. При создании С++ обьекта окна ассоциируешь этот обьект (this) с самим окном (HWND) c помощью ф-ции 
Код
::SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);

  Ну и выбирается этот this в своей статической WNDPROC из HWND:
Код
CMyWindow *This = (CMyWindow*)::GetWindowLong(hWnd, GWL_USERDATA);

  И имея этот This можешь вызывать все свои методы класcа и приватные переменные.
  Пример моего оконного класса диалога, реализующий этот метод, приведен ниже.

3. Реализация приведённая srd.
   Здесь также this связывается с HWND, но уже через др. API ф-цию - именнованное свойство (property) - ф-ция SetProp.
   Ну и выбирается аналогично - внутри своей WNDPROC, через ф-цию GetProp.
   Насколько я знаю, это самый медленный способ.

4. При регистрации своего оконного класса можно указать дополнительное число байтов (extra bytes - WNDCLASSEX::cbClsExtra) которые сама система будет выделять при создании окна. Идея закл. в том, что нужно в качестве этого размера указать размер указателя на свой оконный С++ класс. И далее, при создании системой окна, сохранить в этих дополнительных данный свой this с помощью вызова:
Код
::SetWindowLong(m_hWnd, 0, (LONG)this)

Доставать свой this, опять аналогично - из своей статической ф-ции WNDPROC с пмощь вызова:
Код
CMyWindow *This = (CMyWindow*)::GetWindowLong(hWnd, 0);

Как видно этот метод почти аналогичен методу №2

5. В MFC, вроде бы, реализовано так. Есть глобальная переменная, аналогичная std::map<HWND, CWindow*>. Ну и тоже самое, когда окно создаётся - указатель на С++ обьект сохраняется в глоб. переменной. И выбирается внутри WNDPROC...


Более подробно некорые эти методы описаны в
http://www.catch22.net/tuts/custctrl.asp   в разделе 'Associating a structure with the custom control'

Пример реализации немодально окна About на чистом API:
Код

////////////////////////////////////////////////////////////////////////////////
// file name: "About.h"
////////////////////////////////////////////////////////////////////////////////

#ifndef __FILE__ABOUT__
#define __FILE__ABOUT__

class CDialogAbout {
public:
   HWND       m_hWnd;
private:
   static LPCTSTR IDD() { static TCHAR szResourceName[] = _T("About"); return szResourceName; }
   static INT_PTR CALLBACK DialogProcThis(HWND, UINT, WPARAM, LPARAM);
   INT_PTR DialogProc(HWND, UINT uMsg, WPARAM wParam, LPARAM lParam) {
      switch (uMsg){
      HANDLE_MSG(m_hWnd, WM_INITDIALOG, OnInitDialog);
      HANDLE_MSG(m_hWnd, WM_COMMAND   , OnCommand);
      HANDLE_MSG(m_hWnd, WM_CLOSE     , OnClose);
      HANDLE_MSG(m_hWnd, WM_DESTROY   , OnDestroy);
      }
      return FALSE;
   }
public:
   CDialogAbout() : m_hWnd(NULL) {}
   HWND Create(HWND);
private:
   BOOL OnInitDialog(HWND, HWND, LPARAM);
   void OnCommand   (HWND, int, HWND, UINT);
   void OnClose     (HWND);
   void OnDestroy   (HWND);
};
#endif // __FILE__ABOUT__

Код

////////////////////////////////////////////////////////////////////////////////
// file name: "About.cpp"
////////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include <WindowsX.h>

#include "About.h"

inline LONG SetWindowUserData(HWND hWnd, LONG lUserData) {
   return ::SetWindowLong(hWnd, GWL_USERDATA, lUserData);
}

inline LONG GetWindowUserData(HWND hWnd) {
   return ::GetWindowLong(hWnd, GWL_USERDATA);
}

HWND CDialogAbout::Create(HWND hWndParent) {
   return ::CreateDialogParam(::GetModuleHandle(NULL), CDialogAbout::IDD(), hWndParent, (DLGPROC)CDialogAbout::DialogProcThis, (LPARAM)this);
}

/*static*/ INT_PTR CALLBACK CDialogAbout::DialogProcThis(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
   CDialogAbout *This = NULL;
   if (uMsg == WM_INITDIALOG) {
      This = (CDialogAbout*)lParam;
      if (This) {
         This->m_hWnd = hWnd;
         ::SetWindowUserData(hWnd, (LONG)This);
      }
   } else {
      This = (CDialogAbout*)::GetWindowUserData(hWnd);
   }
   if (This) {
      return This->DialogProc(hWnd, uMsg, wParam, lParam);
   }
   return FALSE;
}

BOOL CDialogAbout::OnInitDialog(HWND, HWND hwndFocus, LPARAM lParam) {
   //...
   return TRUE;
}

void CDialogAbout::OnCommand(HWND, int idControl, HWND hwndCtl, UINT codeNotify) {
   switch (idControl) {
   case IDCANCEL:
   case IDOK:
      if (codeNotify == BN_CLICKED) {
         ::SendMessage(m_hWnd, WM_CLOSE, 0L, 0L);
      }
      return;
   }
}

void CDialogAbout::OnClose(HWND) {
   ::DestroyWindow(m_hWnd);
}

void CDialogAbout::OnDestroy(HWND) {
   m_hWnd = NULL;
}



Обрати внимание, что для диалогов я делаю ассоциацию this с HWND при сообщении WM_INITDIALOG.
А в примере srd - при сообщении WM_NCCREATE. Так правильнее...
PM MAIL   Вверх
SeregaLBN
Дата 1.8.2006, 12:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 178
Регистрация: 3.10.2003
Где: Киев

Репутация: нет
Всего: нет



Ах да... Тема ж называется "RegisterClassEx, Параметр WNDPROC lpfnWndProc"

Вот в догонку класс не диалога (parent окно в виде курсора) (есть вызов RegisterClassEx):
Код

class CCursor
{
public:
   HWND m_hWnd;
private:
   HRGN m_hRgn;

   static LPCTSTR GetWindowClass() {
      const static TCHAR szWndClass[] = _T("CCursor");
      return szWndClass;
   }

   static LRESULT CALLBACK WndProcThis(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
      CCursor *This = NULL;
      if (uMsg == WM_CREATE) {
         CREATESTRUCT *pCS = (CREATESTRUCT*)lParam;
         This = pCS ? (CCursor*)pCS->lpCreateParams : NULL;
         if (This) {
            This->m_hWnd = hWnd;
            ::SetWindowUserData(hWnd, (LONG)This);
         }            
      } else {
         This = (CCursor*)::GetWindowUserData(hWnd);
      }
      if (This) {
         return This->WndProc(hWnd, uMsg, wParam, lParam);
      }

      return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
   }

   LRESULT WndProc(HWND, UINT uMsg, WPARAM wParam, LPARAM lParam) {
      switch(uMsg){
      HANDLE_MSG(m_hWnd, WM_CREATE       , OnCreate);
      HANDLE_MSG(m_hWnd, WM_MOUSEACTIVATE, OnMouseActivate);
      HANDLE_MSG(m_hWnd, WM_DESTROY      , OnDestroy);
      HANDLE_MSG(m_hWnd, WM_NCHITTEST    , OnNCHitTest);
      }
      return ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
   }

public:
   CCursor() : m_hWnd(NULL)
   {
      static WNDCLASSEX wc = {
         sizeof(WNDCLASSEX),                 // UINT        cbSize;
         0,                                  // UINT        style;
         WndProcThis,                        // WNDPROC     lpfnWndProc;
         0,                                  // int         cbClsExtra;
         0,                                  // int         cbWndExtra;
         ::GetModuleHandle(NULL),            // HINSTANCE   hInstance;
         NULL,                               // HICON       hIcon;
         ::LoadCursor(NULL, IDC_ARROW),      // HCURSOR     hCursor;
         (HBRUSH)::GetStockObject(BLACK_BRUSH),  // HBRUSH      hbrBackground;
         //::GetSysColorBrush(COLOR_BTNFACE),  // HBRUSH      hbrBackground;
         NULL,                               // LPCTSTR     lpszMenuName;
         GetWindowClass(),                   // LPCTSTR     lpszClassName;
         NULL                                // HICON       hIconSm;
      };
      static ATOM atom = ::RegisterClassEx(&wc);
   }

   BOOL Create(HWND hWndParent, const RECT &rc) {
      m_hWnd = ::CreateWindowEx(
   #if(_WIN32_WINNT >= 0x0500)
         WS_EX_NOACTIVATE,
   #else
         0,
   #endif
         GetWindowClass(), NULL, WS_POPUP,
         rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top,
         hWndParent, NULL, ::GetModuleHandle(NULL), this);
      return !!m_hWnd;
   }

private:
   BOOL OnCreate(HWND, LPCREATESTRUCT lpCreateStruct) {
      lpCreateStruct->cx = 18; lpCreateStruct->cy = 24; // размеры курсора

      // Создаю регион 'а-ля курсор'
      POINT pArr[] = {{0,0}, {17,17}, {6,17}, {0,23}}; // {{0,0}, {17,17}, {9,17}, {14,28}, {13,29}, {11,29}, {5,18}, {0,23}};
      m_hRgn = ::CreatePolygonRgn(pArr, chDIMOF(pArr), WINDING);
      ::SetWindowRgn(m_hWnd, m_hRgn, TRUE);

      return TRUE;
   }

   void OnDestroy(HWND){
      // удаляю регион
      ::SetWindowRgn(m_hWnd, NULL, FALSE); // освобождаю m_hRgn. При этом m_hRgn само удалится неявно из этой же ф-ции
      m_hRgn = NULL;
   }

   int OnMouseActivate(HWND, HWND hWndTopLevel, UINT codeHitTest, UINT msg) {
      return MA_NOACTIVATE;
   }

   UINT OnNCHitTest(HWND, int x, int y) {
      return HTTRANSPARENT;
   }
};


Здесь реализован ещё один метод, уже 6-ой smile
При создании окна ф-цией CreateWindowEx в последнем параметре модно передать свои данные. И в них я и передаю this.
Эти данные вернуться мне же, в ф-цию WNDPROC при приходе сообщения WM_CREATE, при котором я достаю свой this и уже сохраняю его при помощи ::SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this); для того чтобы в дальнейшем (при приходе др. оконных сообщений) выбирать с пощью GetWindoLong, опять же, свой this.
PM MAIL   Вверх
GremlinProg
Дата 1.8.2006, 12:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Нужно учесть только один момент: WM_NCCREATE - это не первое сообщение, которе приходит в окно. Первым оно будет только если это дочернее окно и оно не имеет стиля WS_THICKFRAME. Поэтому, для надежности, желательно регистрировать любое такое окно с двумя процедурами окна:
1 - процедура инициализации, которая ассоциирует указатель на объект окна с самим окном и установит процедуру окна на ворую, основную;
2 - основная процедура, выделяет ваш объект окна и вызывает его встроенную функцию по обработке очереди сообщений.


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.0538 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.