0

Possible Duplicate:
To pass a pointer to a member function

When I move out of the class the WNDPROC DefEditProc; and EditKeyProc all works ok. But now as I pasted the code it fails compilation with error error: invalid use of member function (did you forget the '()' ?). So my question is how to squeeze this code into class so I do not pollute global namespace ?

#include <windows.h>
#include <richedit.h>

class richEdit {
  HWND richeditWindow;
  WNDPROC DefEditProc;
  public:

  LRESULT EditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    return CallWindowProc(DefEditProc, hwnd, uMsg, wParam, lParam);
  }
  richEdit() {
    HMODULE richedit_library = LoadLibrary("Msftedit.dll");
    if (NULL == richedit_library) abort();

    HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(0);
    richeditWindow = CreateWindowExW (
      WS_EX_TOPMOST,
      MSFTEDIT_CLASS,
      L"window text",
      WS_OVERLAPPED | WS_SYSMENU | ES_MULTILINE | WS_VISIBLE,
      0, 0, 500, 500,
      NULL, NULL, hInstance, NULL
    );
    DefEditProc = (WNDPROC)SetWindowLong(richeditWindow, GWL_WNDPROC, (long)EditKeyProc);
  }
  ~richEdit() {
    MSG msg;
    while( GetMessageW( &msg, richeditWindow, 0, 0 ) ) {
      TranslateMessage( &msg );
      DispatchMessageW( &msg );
    }
  }
};

int main() {
  richEdit re;
}
Community
  • 1
  • 1
rsk82
  • 28,217
  • 50
  • 150
  • 240
  • `#include ` is the worse you can do if you intent to not pollute the global namespace. – K-ballo Jan 17 '13 at 20:54
  • 4
    Just a general note, you should use `SetWindowLongPtr` when setting pointers, for forward-compatibility with 64-bit applications. Read http://msdn.microsoft.com/en-us/library/windows/desktop/ms644898(v=vs.85).aspx – paddy Jan 17 '13 at 21:03

3 Answers3

4

You are supposed to use a free function instead of a member function, since member functions have an implicit this parameter. You will have to declare EditKeyProc as static CALLBACK, and find another way to pass around the this pointer if you need it.

static LRESULT CALLBACK EditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    :::
}

Additionally, you may be able to use SetWindowSubclass which will take care of proper subclassing and will keep around an extra pointer argument for you.

K-ballo
  • 80,396
  • 20
  • 159
  • 169
  • Declaring the callback as a static member works on all Windows compilers that I know of, but do be aware that it isn't portable (according to the Standard, there's a potential issue with language linkage). – Ben Voigt Jan 17 '13 at 21:16
  • @BenVoigt: It should be `__stdcall` as well, right? – K-ballo Jan 17 '13 at 21:19
  • It should be `extern "C" __stdcall`, since that's what Windows expects from pretty much all callback functions. `CALLBACK` should include `__stdcall`. – Ben Voigt Jan 17 '13 at 21:20
  • @BenVoigt: I have never thought of `extern "C"`, good point... – K-ballo Jan 17 '13 at 21:22
  • Yeah, formally the language linkage `extern "C"` not only affects the exported name but is also part of the function type. However on Win32, functions with C language linkage and C++ language linkage are the same type anyway. – Ben Voigt Jan 17 '13 at 21:26
3

Try this:

#include <windows.h>
#include <richedit.h>

class richEdit
{
private:
    HWND richeditWindow;
    WNDPROC DefEditProc;

    static LRESULT CALLBACK EditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        richEdit *pThis = (richEdit*) GetWindowLongPtr(hwnd, GWL_USERDATA);
        return CallWindowProc(pThis->DefEditProc, hwnd, uMsg, wParam, lParam);
    }

public:
    richEdit()
        : richeditWindow(NULL), DefEditProc(NULL)
    {
        HMODULE richedit_library = LoadLibrary("Msftedit.dll");
        if (NULL == richedit_library) abort();

        HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(0);
        richeditWindow = CreateWindowExW (
          WS_EX_TOPMOST,
          MSFTEDIT_CLASS,
          L"window text",
          WS_OVERLAPPED | WS_SYSMENU | ES_MULTILINE | WS_VISIBLE,
          0, 0, 500, 500,
          NULL, NULL, hInstance, NULL
        );
        if (NULL == richeditWindow) abort();

        SetWindowLongPtr(richeditWindow, GWL_USERDATA, (LONG_PTR)this);
        DefEditProc = (WNDPROC) SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)&EditKeyProc);
    }

    ~richEdit()
    {
        if (richeditWindow != NULL)
        {
            SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)DefEditProc);
            DestroyWindow(richeditWindow);
        }
    }
};

Or:

#include <windows.h>
#include <richedit.h>

namespace myNS
{
    LRESULT CALLBACK richEditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    class richEdit
    {
    private:
        HWND richeditWindow;
        WNDPROC DefEditProc;

    public:
        richEdit()
            : richeditWindow(NULL), DefEditProc(NULL)
        {
            HMODULE richedit_library = LoadLibrary("Msftedit.dll");
            if (NULL == richedit_library) abort();

            HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(0);
            richeditWindow = CreateWindowExW (
              WS_EX_TOPMOST,
              MSFTEDIT_CLASS,
              L"window text",
              WS_OVERLAPPED | WS_SYSMENU | ES_MULTILINE | WS_VISIBLE,
              0, 0, 500, 500,
              NULL, NULL, hInstance, NULL
            );
            if (NULL == richeditWindow) abort();

            SetWindowLongPtr(richeditWindow, GWL_USERDATA, (LONG_PTR)this);
            DefEditProc = (WNDPROC) SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)&richEditKeyProc);
        }

        ~richEdit()
        {
            if (richeditWindow != NULL)
            {
                SetWindowLongPtr(richeditWindow, GWL_WNDPROC, (LONG_PTR)DefEditProc);
                DestroyWindow(richeditWindow);
            }
        }
    };

    LRESULT CALLBACK richEditKeyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        richEdit *pThis = (richEdit*) GetWindowLongPtr(hwnd, GWL_USERDATA);
        return CallWindowProc(pThis->DefEditProc, hwnd, uMsg, wParam, lParam);
    }
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

You can't use a pointer to a member function as a window procedure because Windows expects a regular function pointer. Internally, it won't have enough storage for the member pointer in some cases (they can be up to 16 bytes in MSVC, 32-bit), and it won't know what "this" pointer to use in any case.

If your only interest is avoiding namespace pollution, then the easiest solution is to use a namespace instead of a class. Otherwise, you'll have to make EditKeyProc a static member of the class and use SetWindowLong to store a copy of the "this" pointer for EditKeyProc to access.

Peter Ruderman
  • 12,241
  • 1
  • 36
  • 58
  • 1
    He's not trying to "store" a member function, he's trying to hand the function pointer to Windows to be called. Since a pointer-to-member isn't a function pointer, it can't work even if the size is compatible. – Ben Voigt Jan 17 '13 at 21:17
  • That's a good point. I'll clarify my answer. – Peter Ruderman Jan 17 '13 at 21:24