0

I am trying to subclass a Window on Windows (Winapi). Please excuse my lack of knowledge, the Windows style subclassing is so different to what I know from Qt.

So I have a base class Window like this (code excerpt taken from deimos1877), here is the essential part:

class BorderlessWindow
{
  enum class Style : DWORD
  {
    windowed = ( WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN | WS_SYSMENU  ),
    aero_borderless = ( WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN )
  };
public:
  HWND hWnd;
  HINSTANCE hInstance;

  BorderlessWindow( HBRUSH windowBackground, const int x, const int y, const int width, const int height );
  ~BorderlessWindow();
  static LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );
};

The part of the source file:

HWND winId = 0;

BorderlessWindow::BorderlessWindow( HBRUSH windowBackground, const int x, const int y, const int width, const int height )
: hWnd( 0 ),
  hInstance( GetModuleHandle( NULL ) ),
  borderless( false ),
  borderlessResizeable( true ),
  aeroShadow( false ),
  closed( false ),
  visible( false )
{
  WNDCLASSEX wcx = { 0 };
  wcx.cbSize = sizeof( WNDCLASSEX );
  wcx.style = CS_HREDRAW | CS_VREDRAW;
  wcx.hInstance = hInstance;
  wcx.lpfnWndProc = WndProc;
  wcx.cbClsExtra    = 0;
  wcx.cbWndExtra    = 0;
  wcx.lpszClassName = L"WindowClass";
  wcx.hbrBackground = windowBackground;
  wcx.hCursor = LoadCursor( hInstance, IDC_ARROW );
  RegisterClassEx( &wcx );

  if ( FAILED( RegisterClassEx( &wcx ) ) ) {
      throw std::runtime_error( "Couldn't register window class" );
  }

  hWnd = CreateWindow( L"WindowClass", L"Updater", static_cast<DWORD>( Style::windowed ), x, y, width, height, 0, 0, hInstance, nullptr );

  if ( !hWnd ){
      throw std::runtime_error( "Couldn't create window because of reasons" );
  }

  SetWindowLongPtr( hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>( this ) );

  show();
}

LRESULT CALLBACK BorderlessWindow::WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
  std::cout<<"BorderlessWindow::WndProc"<<std::endl;
  BorderlessWindow *window = reinterpret_cast<BorderlessWindow*>( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );
  if ( !window ) return DefWindowProc( hWnd, message, wParam, lParam );

  switch ( message ) {
  // ...
  }
  return DefWindowProc(hWnd, message, wParam, lParam);

At first, all the code from the subclass-to-be was first in the BorderlessWindow class. Essentially, I draw some images and text into the Window. In my first subclass attempt (not shown here) I did not take care of the WndProc, and I think this is the reason why the image and text have not been painted anymore. So here is my last attempt:

class Dialog : public BorderlessWindow
{
public:
    explicit Dialog();
    ~Dialog() = default;

    void updateTitlebar(HWND hwndParent);
    void updateImage(HWND hwndParent);
    void updateText(HWND hwndParent);
    void setupContent(HWND hwndParent);
protected:
    static LRESULT DialogWndProc(HWND hWnd, UINT msg, WPARAM w, LPARAM l, UINT_PTR uid, DWORD_PTR RefData){
        std::cout << "Dialog::SubclassWndProc" << std::endl;
        Dialog *dlg = reinterpret_cast<Dialog*>(RefData);
        if ( !dlg ){
            return DefSubclassProc(hWnd, msg, w, l);
        }
        switch (msg) {
        case WM_PAINT:{
            dlg->updateTitlebar(hWnd);
            dlg->updateText(hWnd);
            dlg->updateImage(hWnd);
            break;
        }
        default:
            return DefSubclassProc(hWnd, msg, w, l);
        }
    }
};

Please note that I changed the signature of the new WndProc to include the parameter UINT_PTR uid, because initially I got this error:

invalid conversion from 
'LRESULT (*)(HWND, UINT, WPARAM, LPARAM, DWORD_PTR) 
{aka long int (*)
(HWND__*, unsigned int, unsigned int, long int, long unsigned int)}' to 
'SUBCLASSPROC 
{aka long int (__attribute__((__stdcall__)) *)
(HWND__*, unsigned int, unsigned int, long int, unsigned int, long unsigned int)}' [-fpermissive]        
SetWindowSubclass(hWnd,DialogWndProc,0,(DWORD_PTR)this);

After adding the parameter, according to the headercommctrl.h

typedef LRESULT (CALLBACK *SUBCLASSPROC)(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam,UINT_PTR uIdSubclass,DWORD_PTR dwRefData);

WINBOOL WINAPI SetWindowSubclass(HWND hWnd,SUBCLASSPROC pfnSubclass,UINT_PTR uIdSubclass,DWORD_PTR dwRefData);

it still does not compile, showing following error:

invalid conversion from 
'LRESULT (*)(HWND, UINT, WPARAM, LPARAM, UINT_PTR, DWORD_PTR) 
{aka long int (*)
(HWND__*, unsigned int, unsigned int, long int, unsigned int, long unsigned int)}' to '
SUBCLASSPROC 
{aka long int (__attribute__((__stdcall__)) *)
(HWND__*, unsigned int, unsigned int, long int, unsigned int, long unsigned int)}' [-fpermissive]
SetWindowSubclass(hWnd,DialogWndProc,NULL,(DWORD_PTR)this);

The signatures are matching now, aren't they? I do not understand the error properly. Am I at least on the right track?

user2366975
  • 4,350
  • 9
  • 47
  • 87
  • Windows is a `C` api and cannot call non-static member functions. Your callback need to be either a top level `C` function or a static member function. `BorderlessWindow::WndProc` is a non-static member function. – Richard Critten Jun 01 '17 at 09:03
  • The error describes very clearly what is missing. The calling convention. I respectfully suggest that you learn to read error messages. The compiler doesn't emit them to annoy you. It emits them to inform you. Be informed. – David Heffernan Jun 01 '17 at 09:05
  • @DavidHeffernan Even so I am wondering how `wcx.lpfnWndProc = WndProc;` compiles and I think will be source of on-going problems. – Richard Critten Jun 01 '17 at 09:07
  • @RichardCritten It compiles because it is declared correctly. It is `static`. – David Heffernan Jun 01 '17 at 09:10
  • A `non-static member function`? It has the `static` keyword in the header signature, so why is is not static? The code from deimos1877 itself is working. And isn't my own WndProc static as well? – user2366975 Jun 01 '17 at 09:11
  • @DavidHeffernan Thank you for your suggestion. Well up to now I did not have such errors, like I said the whole Winapi stuff is quite different to me. I did not know about __stdcall before. Besides, for any future readers, the painting is working correctly now. – user2366975 Jun 01 '17 at 09:23
  • Sorry, I feel stupid now, miss-read the header file. – Richard Critten Jun 01 '17 at 09:25
  • 1
    Since you are registering your own window class, consider placing the instance pointer in the extra window memory. `GWLP_USERDATA` is unreliable in practice, because everyone has access to it (and abuses it at will). – IInspectable Jun 01 '17 at 11:01
  • You should also pass the `this` pointer in the `lpParam` parameter of `CreateWindow()` and then call `SetWindowLongPtr()` inside of `WndProc()` when it receives `WM_NCCREATE` or `WM_CREATE`. Don't wait until `CreateWindow()` returns before calling `SetWindowLongPtr()`. There are quite a few messages sent during window creation, waiting until `CreateWindow()` returns would cause `WndProc()` to ignore them all. – Remy Lebeau Jun 01 '17 at 19:34

1 Answers1

0

They are not quite the same. You need to change the declaration to:

static LRESULT __stdcall DialogWndProc(HWND hWnd, UINT msg, 
                    WPARAM w, LPARAM l, UINT_PTR uid, DWORD_PTR RefData)

Notice the __stdcall

  • Thank you. I additionally replaced `__stdcall` with `WINAPI`. – user2366975 Jun 01 '17 at 09:20
  • 1
    @user Why `WINAPI`? The declaration has it as `CALLBACK`. I know that they typically expand to the same thing, but why not be precise? – David Heffernan Jun 01 '17 at 09:22
  • I searched for `__stdcall` and found this: https://stackoverflow.com/questions/297654/what-is-stdcall. And because I read WINAPI everywhere in windows programming, I thought it would look more "Windows like". – user2366975 Jun 01 '17 at 09:24
  • @user That sounds like programming by random guesswork. I recommend that you study documentation and header files to learn what the correct prototype is for callbacks. – David Heffernan Jun 01 '17 at 09:27
  • You are right. Considering this doc https://msdn.microsoft.com/en-us/library/windows/desktop/bb776774(v=vs.85).aspx it is not obvious to me why I should need __stdcall. Guess this is what you mean by studying. I also did see officiall docs and examples which did not work (e..g wrong signature) and someone else then told me the docs are just wrong. – user2366975 Jun 01 '17 at 09:35