Как эффективный способ обернуть HWND в объекты в C ++? - PullRequest
3 голосов
/ 26 июня 2010

Я работал с C ++ и Win32 (не MFC / ATL). Я работаю над написанием своей собственной библиотеки классов для обертывания определенных объектов Win32 (в частности, HWND).

Когда дело доходит до создания оконЯ считаю метод RegisterClassEx / CreateWindowEx очень неудобным.Этот дизайн затрудняет написание простых оболочек классов (нужно прибегнуть к thunks, или TLS, или какому-либо другому сложному механизму).

Мне кажется, было бы проще просто позволить приложению указать оконную процедуруи указатель пользовательских данных во время создания окна.

Есть ли какая-то очевидная причина, по которой мне не хватает выбора дизайна здесь?Есть ли действительно простой и эффективный способ для этого?

Ответы [ 2 ]

7 голосов
/ 26 июня 2010

ATL CWindow и CWindowImpl - ваши друзья.

CWindowImpl заботится о неловкости RegisterClass / CreateWindow, о которой вы говорите.

CWindow - базовый класс-оболочка для HWNDвсе функции win32 обобщены.

Причина, по которой я предпочитаю ATL, а не MFC.ATL - это очень легкий набор классов со всем предоставленным исходным кодом.Это простой #include без каких-либо дополнительных библиотек или сред выполнения.После многих лет прокручивания моих собственных классов WndProcs и классов инкапсуляции окон я нашел, что работать с CWindowImpl очень приятно.Вы должны объявить глобальный экземпляр AtlModuleExe в своем коде, чтобы использовать его, но, кроме того, ATL остается в стороне.

Ссылки на документацию для этих классов ниже: CWindow: http://msdn.microsoft.com/en-us/library/d19y607d.aspx

CWindowImpl: http://msdn.microsoft.com/en-us/library/h4616bh2.aspx

Обновление: вот пример кода, который я выкопал для вас:

class CMyApp : public CAtlExeModuleT<CMyApp>
{
public:
    static HRESULT InitializeCom()
    {
        CoInitialize(NULL);
        return S_OK;
    }
};

CMyApp g_app;

class CMyWindow : public CWindowImpl<CMyWindow>
{
public:
    CMyWindow();
    ~CMyWindow();
    BEGIN_MSG_MAP(CMyWindow)
        MESSAGE_HANDLER(WM_PAINT, OnPaint);
        MESSAGE_HANDLER(WM_CLOSE, OnClose);
    END_MSG_MAP();

    LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled);
    LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled);
};

LRESULT CMyWindow::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
   // your WM_PAINT code goes here
}

LRESULT CMyWindow::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
    PostQuitMessage();
}

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdline, int nCmdShow)
{
    // 640x480 window
    CMyWindow appwindow;
    RECT rect = {0, 0, 640, 480};
    RECT rectActual = {0};
    appwindow.Create(NULL, rect, L"App Window", WS_OVERLAPPEDWINDOW);
    appwindow.ShowWindow(SW_SHOW);

    {
        MSG msg;
        while (GetMessage(&msg, 0, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    // app shutdown

    appwindow.DestroyWindow();

    return 0;
}
1 голос
/ 26 июня 2010

прибегают к тханам или тлс? Я не знаю, что вы подразумеваете под thunk в этом случае, но довольно просто - хотя бы немного запутаться - загрузить окно в оболочку класса c ++.

class UserWindow
{
  HWND _hwnd;
public:
  operator HWND(){
    return _hwnd;
  }
  UserWindow():_hwnd(0){}
  ~UserWindow(){
    if(_hwnd){
      SetWindowLongPtr(GWL_USERDATA,0);
      DestroyWindow(_hwnd);
  }
  static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
    UserWindow* self = 0;
    if(uMsg == WM_CREATE)
    {
      LPCREATESTRUCT crst = (LPCREATESTRUCT)lParam;
      self = (Window*)crst->lpCreateParams;
      SetWindowLongPtr(hwnd,GWL_USERDATA,(LONG_PTR)self);
      self->_hwnd = hwnd;
    }
    else
      self = (Window*)GetWindowLongPtr(hwnd,GWL_USERDATA);

    if(self){
      LRESULT lr = self->WndProc(uMsg,wParam,lParam);
      if(uMsg == WM_DESTROY){ 
        if(self = (Window*)GetWindowLongPtr(hwnd,GWL_USERDATA))
          self->_hwnd = NULL;
      }
      return lr;
    }
    return DefWindowProc(hwnd,uMsg,wParam,lParam);
  }
  HWND Create(int x, int y, int w, int h, LPCTSTR pszTitle,DWORD dwStyle,DWORD dwStyleEx,LPCTSTR pszMenu,HINSTANCE hInstance, HWND hwndParent){
    WNDCLASSEX wcex = { sizeof (wcex),0};
    if(!GetClassInfo(hInstance,ClassName(),&wcex)){
      wcex.style = CS_HREDRAW | CS_VREDRAW;
      wcex.lpfnWndProc = WindowndProc;
      wcex.cbClsExtra = 0;
      wcex.cbWndExtra = 0;
      wcex.hInstance = hInstance;
      wcex.lpszClassName = ClassName();
      OnCreatingClass( wcex );
  RegisterClassEx(&wcex);
    }
    return CreateWindowEx( dwStyleEx, ClassName(), pszTitle, dwStyle, x, y, w, h, hwndParent, pszMenu, hInstance, this);
  }
  // Functions to override
  virtual LPCTSTR ClassName(){
    return TEXT("USERWINDOW");
  }
  virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam,LPARAM lParam){
    return DefWindowProc(uMsg,wParam,lParam);
  }
  virtual void Window::OnCreatingClass(WNDCLASSEX& wcex){
    wcex.hCursor = LoadCursor(NULL,IDC_ARROW);
  }
};

Все это немного запутано, но это означает, что окно можно безопасно уничтожить, удалив класс, ИЛИ уничтожив. Во время вызова CreateWindow отправляются одно или два сообщения, относящихся к размеру, прежде чем WM_CREATE устанавливает GWL_USERDATA в «this», но практически они не имеют никакого значения. Класс окна создается автоматически при первом создании окна.


Одна вещь, которую этот стиль автоматической регистрации класса при первом вызове для создания не поддерживает, - это создание экземпляров этого типа окна в качестве элемента управления в диалоге. Для поддержки этого случая необходимо изменить целый ряд вещей. ... предоставить функцию регистрации статического класса ... "новый MyClass" в статическом обработчике WM_CREATE ... для меня не очевидно, как это можно сделать в каркасном стиле.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...