How to change WndProc for a listview control - c++

I am trying to change a Wndproc for a listview control so the first parameter in wndproc return the control handle that receives the message, the problem is when I change it other functions stop working (I cannot insert columns or items anymore), is there something I need to change or return in order to keep using the same wndproc for all the controls
WNDPROC same for all controls:
LRESULT CALLBACK staticWndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
std::cout << (int)hwnd << " control received msg:" << uMsg << std::endl; //This must work
//event = { msg:uMsg, target:(int)hwnd, x:0, y:0, button:0, key:0 };
switch (uMsg){
case WM_DESTROY:
std::cout << "window says bye " << std::endl;
PostQuitMessage(WM_QUIT);
break;
default:
//msghandlercall(event); //this is handled not in c++
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
after calling to SetWindowLongPtr(handle, GWLP_WNDPROC, (LONG_PTR)staticWndProc); insert messages are not working as default
int createColumn(HWND listhandle, int indexCol, char *Text, int width){
LVCOLUMN lvc={0};
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
lvc.cx = width;
lvc.pszText = Text;
lvc.iSubItem = indexCol;
//return ListView_InsertColumn(listhandle, indexCol, &lvc);
SendMessage(listhandle,LVM_INSERTCOLUMN,indexCol,(LPARAM)&lvc);
return 1;
}
void createColumns(HWND listhandle, std::vector<LPSTR> columns){
for(int i=0; i<columns.size(); i++) createColumn(listhandle, i, columns[i], 50);
}
int createItem(HWND listhandle, const std::vector<LPSTR>& row){
LVITEM lvi = {0};
//lvi.mask = LVIF_TEXT;
// lvi.pszText = row[0];
int ret = ListView_InsertItem(listhandle, &lvi);
if(ret>-1) for(unsigned i=0; i<row.size(); i++)
ListView_SetItemText(listhandle, ret, i, row[i]);
return ret;
}
HWND createList(int parenthandle=0){
if(parenthandle==0) parenthandle=(int)GetDesktopWindow();
HWND handle = CreateWindow(WC_LISTVIEW, "",WS_VISIBLE|WS_BORDER|WS_CHILD | LVS_REPORT | LVS_EDITLABELS,
10, 10, 300, 100, (HWND)parenthandle, /*(HMENU)ID_LIST*/NULL, GetModuleHandle(NULL), 0);
if(!handle){ std::cerr << "Failed to create list\n"; return 0; }
SetWindowLongPtr(handle, GWLP_WNDPROC, (LONG_PTR)staticWndProc);
ListView_SetExtendedListViewStyle(handle, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER);
createColumns(handle,{"col1","col2","col3"});
createItem(handle,{"item1.1","item1.2","item1.3","item1.4"});
createItem(handle,{"item2.1","item2.2","item2.3","item2.4"});
return handle;
}
NOTE I tried adding wndproc with SetWindowSubclass() but an error occurs:
error C2664: 'BOOL IsolationAwareSetWindowSubclass(HWND,SUBCLASSPROC,UINT_PTR,DWORD_PTR)': cannot convert argument 2 from 'LRESULT (__cdecl *)(HWND,UINT,WPARAM,LPARAM)' to 'SUBCLASSPROC' [build\binding.vcxproj]

When you subclass a window procedure using SetWindowLongPtr(GWLP_WNDPROC), it returns the previous window procedure that is being replaced. Your subclass procedure must pass unhandled messages to that previous procedure using CallWindowProc() instead of DefWindowProc(). This is stated as much in the documentation:
Calling SetWindowLongPtr with the GWLP_WNDPROC index creates a subclass of the window class used to create the window. An application can subclass a system class, but should not subclass a window class created by another process. The SetWindowLongPtr function creates the window subclass by changing the window procedure associated with a particular window class, causing the system to call the new window procedure instead of the previous one. An application must pass any messages not processed by the new window procedure to the previous window procedure by calling CallWindowProc. This allows the application to create a chain of window procedures.
You are not doing that, which is why your code is failing.
Try this instead:
WNDPROC prevWndProc;
LRESULT CALLBACK staticWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//...
return CallWindowProc(prevWndProc, hwnd, uMsg, wParam, lParam);
}
HWND createList(HWND parenthandle = NULL)
{
if (!parenthandle) parenthandle = GetDesktopWindow();
HWND handle = CreateWindow(..., parenthandle, ...);
if (!handle) {
std::cerr << "Failed to create list\n";
return NULL;
}
prevWndProc = (WNDPROC) SetWindowLongPtr(handle, GWLP_WNDPROC, (LONG_PTR)staticWndProc);
if (!prevWndProc) {
std::cerr << "Failed to subclass list\n";
DestroyWindow(handle);
return NULL;
}
...
return handle;
}
That being said, you really should not be using SetWindowLongPtr(GWLP_WNDPROC) at all. It is unsafe for exact this reason (amongst others). You should be using SetWindowSubclass() instead:
LRESULT CALLBACK staticSubClass(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
//...
if (uMsg == WM_NCDESTROY) {
// NOTE: this requirement is NOT stated in the documentation,
// but it is stated in Raymond Chen's blog article...
RemoveWindowSubclass(hWnd, staticSubClass, uIdSubclass);
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
HWND createList(HWND parenthandle = NULL)
{
if (!parenthandle) parenthandle = GetDesktopWindow();
HWND handle = CreateWindow(..., parenthandle, ...);
if (!handle) {
std::cerr << "Failed to create list\n";
return NULL;
}
if (!SetWindowSubclass(handle, staticSubClass, 1, 0)){
std::cerr << "Failed to subclass list\n";
DestroyWindow(handle);
return NULL;
}
...
return handle;
}
See MSDN for more details:
Subclassing Controls
Safer subclassing

Related

Properly using AddClipboardFormatListener and subscribing to WM_CLIPBOARDUPDATE message

I am currently attempting to use the Windows clipboard and its notifications in my application. Specifically, I am attempting to subscribe to the WM_CLIPBOARDUPDATE window message by using the AddClipboardFormatListener() function. Previously, I had been using the SetClipboardViewer() function in order to add my window directly into the clipboard viewer chain. This had worked just fine, and I had received the relevant messages WM_DRAWCLIPBOARD and WM_DESTROYCLIPBOARD when expected. However, I would like to avoid continuing to use the clipboard chain because of how volatile it can be.
My understanding was that I would be perfectly able to receive WM_CLIPBOARDUPDATE after calling AddClipboardFormatListener(). Is there another step here that I am missing? What do I need to do to make sure that I receive this message properly? As it stands currently, I am not receiving it when performing a copy operation.
Here is an abridged example of what my code looks like:
WNDPROC override:
LRESULT CALLBACK ClipboardService::CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
switch ( pMsg->message )
{
case WM_DRAWCLIPBOARD:
// Handle clipboard available event and forward message
break;
case WM_CLIPBOARDUPDATE:
// This is never triggered
break;
case WM_DESTROYCLIPBOARD:
// Handle clipboard cleared event and forward message
break;
}
return ::CallNextHookEx( g_Hook, nCode, wParam, lParam );
}
Called by Constructor:
HRESULT ClipboardService::SetOrRefreshWindowsHook()
{
HRESULT hr = S_OK;
try
{
if (!m_bHookSet)
{
g_hwndCurrent = ::CreateWindowEx(0, "Message", "ClipboardMessageWindow", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
m_dwThreadID = ::GetWindowThreadProcessId(g_hwndCurrent, &m_dwProcessID);
_Module.Lock();
SetLastError(0);
g_Hook = ::SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, 0, m_dwThreadID);
//g_hwndNext = ::SetClipboardViewer(g_hwndCurrent); old way to subscribe
// This is what I expect should subscribe me to WM_CLIPBOARDUPDATE messages
if (!::AddClipboardFormatListener(g_hwndCurrent))
hr_exit(E_UNEXPECTED);
DWORD dwLastError = ::GetLastError();
g_This = this;
m_bHookSet = true;
}
}
catch (...)
{
hr_exit(E_UNEXPECTED);
}
wrapup:
return hr;
}
This is a COM interface that is called by a .NET wrapper, but I don't think that either of those two things are relevant to my problem in this case (figured I would add just in case).
You should not be using SetWindowsHookEx(WH_CALLWNDPROC) to receive messages to your own window. Use RegisterClass/Ex() instead to register your own custom window class that has your WndProc assigned to it, and then CreateWindowEx() can create an instance of that window class. No hooking needed.
HINSTANCE g_hThisInst = NULL;
HWND g_hwndCurrent = NULL;
//HWND g_hwndNext = NULL;
bool g_AddedListener = false;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
g_hThisInst = hinstDLL;
return TRUE;
}
LRESULT CALLBACK ClipboardService::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
//g_hwndNext = ::SetClipboardViewer(hwnd);
g_AddedListener = ::AddClipboardFormatListener(hwnd);
return g_AddedListener ? 0 : -1;
case WM_DESTROY:
/*
ChangeClipboardChain(hwnd, g_hwndNext);
g_hwndNext = NULL;
*/
if (g_AddedListener)
{
RemoveClipboardFormatListener(hwnd);
g_AddedListener = false;
}
return 0;
/*
case WM_CHANGECBCHAIN:
if (g_hwndNext == (HWND)wParam)
g_hwndNext = (HWND)lParam;
else if (g_hwndNext)
SendMessage(g_hwndNext, uMsg, wParam, lParam);
break;
case WM_DRAWCLIPBOARD:
// Handle clipboard available event
if (g_hwndNext)
SendMessage(g_hwndNext, uMsg, wParam, lParam);
break;
*/
case WM_CLIPBOARDUPDATE:
// Handle clipboard updated event
return 0;
case WM_DESTROYCLIPBOARD:
// Handle clipboard cleared event and forward message
break;
}
return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
HRESULT ClipboardService::SetOrRefreshWindowsHook()
{
try
{
if (!g_hwndCurrent)
{
WNDCLASS wndClass = {};
wndClass.lpfnWndProc = &ClipboardService::WndProc;
wndClass.hInstance = g_hThisInst;
wndClass.lpszClassName = TEXT("ClipboardMessageWindow");
if (!::RegisterClass(&wndClass))
{
DWORD dwLastError = ::GetLastError();
if (dwLastError != ERROR_CLASS_ALREADY_EXISTS)
return HRESULT_FROM_WIN32(dwLastError);
}
g_hwndCurrent = ::CreateWindowEx(0, wndClass.lpszClassName, "", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, g_hThisInst, NULL);
if (!g_hwndCurrent)
{
DWORD dwLastError = ::GetLastError();
return HRESULT_FROM_WIN32(dwLastError);
}
g_This = this;
}
}
catch (...)
{
return E_UNEXPECTED;
}
return S_OK;
}

Showing Win32 common dialog at desktop center from a console program [duplicate]

This is my attempt using a hook function to get the dialog window handle. Both SetWindowPos() and GetLastError() return correct values, but the dialog window is unaffected and shown at position 0,0.
#include <windows.h>
#include <iostream>
static UINT_PTR CALLBACK OFNHookProc (HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
using namespace std;
switch (uiMsg) {
case WM_INITDIALOG: {
// SetWindowPos returns 1
cout << SetWindowPos(hdlg, HWND_TOPMOST, 200, 200, 0, 0, SWP_NOSIZE ) << endl;
// GetLastError returns 0
cout << GetLastError() << endl;
break;
}
}
return 0;
}
int main() {
OPENFILENAMEW ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(OPENFILENAMEW);
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_ENABLEHOOK;
ofn.lpfnHook = OFNHookProc;
GetOpenFileNameW(&ofn);
return 0;
}
When using OFN_EXPLORER, you have to move hdlg's parent window, as the HWND passed to your callback is not the actual dialog window. This is clearly stated in the documentation:
OFNHookProc callback function
hdlg [in]
A handle to the child dialog box of the Open or Save As dialog box. Use the GetParent function to get the handle to the Open or Save As dialog box.
Also, you should wait for the callback to receive the CDN_INITDONE notification, instead of the WM_INITDIALOG message.
Try this:
static UINT_PTR CALLBACK OFNHookProc (HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
if ((uiMsg == WM_NOTIFY) &&
(reinterpret_cast<OFNOTIFY*>(lParam)->hdr.code == CDN_INITDONE))
{
SetWindowPos(GetParent(hdlg), HWND_TOPMOST, 200, 200, 0, 0, SWP_NOSIZE);
}
return 0;
}

How can I retrieve the message-sending object from WndProc's parameters?

So I'm writing my own little GUI framework, wrapping the Windows API in useful classes. I'm currently trying to have the user handle WndProc's messages in an object-oriented way, but I've hit a bit of a snag.
I have a Button class, that inherits from an abstract Control class, that is pretty much a wrapper around the Button's HWND handle.
There's also a Window class, that contains (what do you know) a handle to a window. This class also has a container of Controls, and is responsible for creating its own (static) WndProc method.
What I'm trying to do is have a big switch statement in the containing window's WndProc that, based on the function's wParam and lParam parameters, calls the appropriate sending control's handling function. The way I've seen most people do something like it is this:
#define MY_BUTTON 101 //Define button's ID
Button::Button()
{
hwndButton= CreateWindow(
"BUTTON", text,
WS_VISIBLE | WS_CHILD,
position.x, position.y,
size.x, size.y,
parent_handle,
(HMENU)MY_BUTTON,
GetModuleHandle(NULL),
NULL);
}
Button* button = new Button();
//Later on, in the Window class...
LRESULT CALLBACK Window::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch((LOWORD)wParam)
{
case MY_BUTTON:
button->HandleMessage(&msg);
break;
}
}
However, I don't want the user to assign a unique integer for every object instance they create.
Rather, since I have a container of controls, I would rather do something like this:
//In the Window Class...
Button* button = new Button();
AddControl(button);
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(&msg)
{
ObtainControlFromParams(wParam, lParam)->HandleMessage(&msg);
//ObtainControlFromParams should return a pointer to a Control,
//and it should look into the window's Control collection.
}
}
For as great as this sounds, I can't find a way to implement the ObtainControlFromParams function.
The way I thought of to distinguish every control instance is to have a string, that I call the control's "name", that should be unique to every object instantiation. This would leave me with two options (that I can think of)
Convert the string to a hash, store it somewhere else (such as in a containing window) and use that as the Button's HMENU. I could then use a hash set to link each string hash to its owner object, and call the corrisponding method in that way.
Use the lParam method, that (to the best of my knowledge) stores the button's HWND handle, and do something with that instead.
I apologize if what I'm trying to obtain isn't really clear, it's kind of a complicated concept to me.
Any help is greatly appreciated!
The usual approach is to reflect messages like WM_COMMAND and WM_NOTIFY, which are sent to the control's parent window, back to the control that sent them. This is possible because these messages identify the sender (each in a unique way, so check the docs).
There are a number of ways to associate a C++ object pointer with a window:
Dynamically generated trampoline function as window-proc.
Most efficient but also most tricky. Probably needs to use VirtualAlloc.
Window properties.
The SetProp and GetProp functions.
Window object words.
SetWindowLongPtr. Needs to be sure there's allocated space.
Static map.
E.g. a singleton std::unordered_map, mapping handle → object.
Whatever's used by Windows standard subclassing.
I.e. using SetWindowSubclass.
Store the button's Control* object pointer in the button HWND itself where the message procedure can retrieve it. You can use (Set|Get)WindowLongPtr(GWL_USERDATA) or (Get|Set)SetProp() for that purpose.
Only certain messages, like WM_COMMAND and WM_NOTIFY, identify the control that sends them. These messages are sent to the control's parent window. These messages contain the child control's HWND. The parent can retrieve the child's Control* and forward the message to it.
Other messages are sent directly to the control's own window, not its parent window. These messages do not identify the control they are being sent to. As such, each Control needs its own individual WndProc procedure for its own HWND. Use SetWindowLongPtr(GWL_WNDPROC) or SetWindowSubclass() to assign that WndProc to the HWND after CreateWindow() is called.
Try something like this (this is very rough, there is a lot more involved in wtiting a UI framework, but this should give you some ideas):
typedef std::basic_string<TCHAR> tstring;
class Control
{
private:
HWND fWnd;
Control *fParent;
POINT fPosition;
SIZE fSize;
tstring fText;
std::list<Control*> fControls;
...
void addControl(Control *c);
void removeControl(Control *c);
virtual void createWnd() = 0;
void internalCreateWnd(LPCTSTR ClassName, DWORD style, DWORD exstyle);
...
static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
protected:
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
...
public:
Control();
virtual ~Control();
HWND getWnd();
void destroyWnd();
void wndNeeded();
Control* getParent();
void setParent(Control *value);
POINT getPosition();
void setPosition(POINT value);
SIZE getSize();
void setSize(SIZE value);
tstring getText();
void setText(const tstring &value);
...
};
static const LPTSTR szControlPropStr = TEXT("ControlPtr")
Control* ControlFromWnd(HWND hwnd)
{
return (Control*) GetProp(hwnd, szControlPropStr);
}
Control::Control()
: fWnd(0), fParent(0)
{
fPosition.x = fPosition.y = 0;
fSize.cx = fSize.cy = 0;
...
}
Control::~Control()
{
setParent(0);
destroyWnd();
}
void Control::addControl(Control *c)
{
fControls.push_back(c);
c->fParent = this;
if (fWnd)
c->wndNeeded();
}
void Control::removeControl(Control *c)
{
fControls.remove(c);
c->destroyWnd();
c->fParent = 0;
}
HWND Control::getWnd()
{
wndNeeded();
return fWnd;
}
void Control::internalCreateWnd(LPCTSTR ClassName, DWORD style, DWORD exstyle)
{
style |= WS_VISIBLE;
if (fParent)
style |= WS_CHILD;
fWnd = CreateWindowEx(exstyle,
ClassName, fText.c_cstr(), style,
fPosition.x, fPosition.y,
fSize.cx, fSize.cy,
(fParent) ? fParent->getWnd() : 0,
0,
GetModuleHandle(NULL),
NULL);
SetProp(fWnd, szControlPropStr, (HANDLE)this);
SetWindowLongPtr(fWnd, GWL_WNDPROC, (LONG_PTR)&Control::WndProc);
}
void Control::destroyWnd()
{
DestroyWindow(fWnd);
fWnd = 0;
}
void Control::wndNeeded()
{
if (!fWnd)
{
createWnd();
for (std::list<Control*>::iterator iter = fControls.begin(); iter != fControls.end(); ++iter)
iter->wndNeeded();
}
}
Control* Control::getParent()
{
return fParent;
}
void Control::setParent(Control *value)
{
if (fParent != value)
{
if (fParent)
fParent->removeControl(this);
fParent = value;
if (fParent)
fParent->addControl(this);
}
}
POINT Control::getPosition()
{
return fPosition;
}
void Control::setPosition(POINT value)
{
fPosition = value;
if (fWnd)
SetWindowPos(fWnd, 0, fPosition.x, fPosition.y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}
SIZE Control::getSize()
{
return fSize;
}
void Control::setSize(SIZE value)
{
fSize = value;
if (fWnd)
SetWindowPos(fWnd, 0, 0, 0, fSize.cx, fSize.cy, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
}
tstring Control::getText()
{
return fText;
}
void Control::setText(const tstring &value)
{
fText = value;
if (fWnd)
SetWindowText(fWnd, fText.c_str());
}
LRESULT CALLBACK Control::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Control *pThis = ControlFromWnd(hwnd);
if (pThis)
return pThis->HandleMessage(uMsg, wParam, lParam);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT Control::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
WM_NCDESTROY:
{
RemoveProp(fWnd, szControlPropStr);
fWnd = 0;
break;
}
case WM_COMMAND:
{
HWND hwnd = (HWND) lParam;
if ((hwnd) && (hwnd != fWnd))
{
Control *c = ControlFromWnd(hwnd);
if (c)
return c->HandleMessage(uMsg, wParam, lParam);
}
...
break;
}
case WM_NOTIFY:
{
NMHDR *hdr = (NMHDR*) lParam;
if ((hdr->hwndFrom) && (hdr->hwndFrom != fWnd))
{
Control *c = ControlFromWnd(hdr->hwndFrom);
if (c)
return c->HandleMessage(uMsg, wParam, lParam);
}
...
break;
}
case WM_WINDOWPOSCHANGED:
{
WINDOWPOS *p = (WINDOWPOS*) lParam;
if (!(p->flags & SWP_NOMOVE))
{
fPosition.x = p->x;
fPosition.y = p->y;
}
if (!(p->flags & SWP_NOSIZE))
{
fSize.cx = p->cx;
fSize.cy = p->cy;
}
...
return 0;
}
case WM_SETTEXT:
{
LRESULT ret = DefWindowProc(fWnd, uMsg, wParam, lParam);
if (ret == TRUE)
fText = (TCHAR*) lParam;
return ret;
}
...
}
return DefWindowProc(fWnd, uMsg, wParam, lParam);
}
class Button : public Control
{
protected:
virtual void createWnd();
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
public:
Button();
};
Button::Button()
: Control()
{
}
void Button::createWnd()
{
Control::intetnalCreateWnd(TEXT("BUTTON"), BS_PUSHBUTTON | BS_TEXT, 0);
...
}
LRESULT Button::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_COMMAND:
{
HWND hwnd = (HWND) lParam;
if (hwnd != fWnd)
break;
switch (HIWORD(wParam))
{
case BN_CLICKED:
{
...
return 0;
}
...
}
break;
}
}
return Control::HandleMessage(uMsg, wParam, lParam);
}
//In the Window Class...
Button* button = new Button();
button->setPosition(...);
button->setSize(...);
button->setText(...);
button->setParent(this);

Windows C++ - Creating window in a class makes it look unusual

Ok, so i made a class which creates a HWND.
However, the window created shows some strange properties: it is not like other windows - it's non-transparent, the close-minimize-maximize buttons are located differently from normal windows.
But the style specified is default (WM_OVERLAPPEDWINDOW).
What's more, it can't be closed unless i move it a bit (seems like it is not generating WM_DESTROY or WM_CLOSE messages before moving).
This might be a problem with the implementation of main WinProc calling another message processer using pointers. However, i have no idea why the window is unusually looking.
My code:
//mywind.h
class Window
{
private:
HWND mHwnd;
const char* className="Window";
static LRESULT CALLBACK StartWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); //main WindowProc function
LRESULT ThisWindowProc(UINT msg, WPARAM wParam, LPARAM lParam); //Another, object-specific message processing function
bool isClassRegistered(HINSTANCE hinst);
public:
Window() : mHwnd( 0 ) { }
~Window();
int create(std::string title, int width, int height);
};
//mywind.cpp
Window::~Window()
{
if( mHwnd ) DestroyWindow( mHwnd );
}
int Window::create(std::string title, int width, int height)
{
WNDCLASS wincl;
HINSTANCE hInst = NULL;
hInst = GetModuleHandle(NULL);
if(hInst==NULL)
{
printf("Failed to load hInstance\n");
return -1;
}
if(!GetClassInfo(hInst, className, &wincl))
{
printf("Getting class info.\n");
wincl.style = 0;
wincl.hInstance = hInst;
wincl.lpszClassName = className;
wincl.lpfnWndProc = StartWindowProc;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hIcon = NULL;
wincl.hCursor = NULL;
wincl.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wincl.lpszMenuName = NULL;
if(!isClassRegistered(hInst))
{
if (RegisterClass(&wincl) == 0)
{
printf("The class failed to register.\n");
return 0;
}
}
}
mHwnd = CreateWindow(className, title.c_str(), WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, width, height,
NULL, NULL, hInst, this);
if(mHwnd==NULL)
{
printf("Failed to create HWND.\n");
return -1;
}
MSG msg;
while(GetMessage(&msg, mHwnd, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
printf("Destroying window.\n");
if(mHwnd) DestroyWindow(mHwnd);
mHwnd=NULL;
printf("Returning.\n");
return msg.wParam;
}
bool Window::isClassRegistered(HINSTANCE hinst)
{
WNDCLASSEX clinf;
if(!GetClassInfoEx(hinst, className, &clinf)) return false;
return true;
}
LRESULT CALLBACK Window::StartWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
Window* winp = NULL;
if(msg == WM_CREATE)
{
CREATESTRUCT* cs = (CREATESTRUCT*) lParam;
winp = (Window*) cs->lpCreateParams;
SetLastError(0);
if(SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) winp) == 0)
{
if(GetLastError()!=0) return -1;
}
}
else
{
winp = (Window*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if(winp) return winp->ThisWindowProc(msg, wParam, lParam);
return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT Window::ThisWindowProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_PAINT:
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(mHwnd, msg, wParam, lParam);
}
return 0;
}
//main.cpp
#include "mywind.h"
int main(int argc, char* argv[])
{
Window mwnd;
mwnd.create("Test", 200, 200);
return 0;
}
Notice that you don't set mHwnd until CreateWindowEx returns. This means that all the messages sent during window creation pass 0 to DefWindowProc instead of the actual window handle. This causes the window manager to think you are bypassing default handling and doing custom captions, which is why everything looks wrong.
TL;DR: Set mHwnd inside your WM_NCCREATE handler.
I am 90% sure that your problem is the missing manifest which enables WinXP/7 look. Now you are in compatibility mode and the window frame is for Windows 98/2000 style. For more details read this link (or many others for the same problem):
http://www.mctainsh.com/Articles/Csharp/XpControlsInCS.aspx

Win32 C++ Create a Window and Procedure Within a Class

Pre-Text/ Question
I am trying to make a fairly simple tool to help debug variable values. For it to be completely self contained within the class is what I am aiming for. The end product I can use a function in the class like ShowThisValue(whatever).
The problem I am having is that I can't figure out, if possible, to have the procedure within the class. Here is the short version, with the problem.
-Code updated again 11/29/13-
-I have put this in its own project now.
[main.cpp]
viewvars TEST; // global
TEST.CreateTestWindow(hThisInstance); // in WinMain() right before ShowWindow(hwnd, nFunsterStil);
[viewvars.h] The entire updated
class viewvars {
private:
HWND hWindow; // the window, a pointer to
LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
public:
viewvars(); // blank constructor
int CreateTestWindow(HINSTANCE hInst);
};
// blank constructor
viewvars::viewvars() {}
// create the window
int viewvars::CreateTestWindow(HINSTANCE hInst) {
// variables
char thisClassName[] = "viewVars";
MSG msg;
WNDCLASS wincl;
// check for class info and modify the info
if (!GetClassInfo(hInst, thisClassName, &wincl)) {
wincl.style = 0;
wincl.hInstance = hInst;
wincl.lpszClassName = thisClassName;
wincl.lpfnWndProc = &ThisWindowProc;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hIcon = NULL;
wincl.hCursor = NULL;
wincl.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
wincl.lpszMenuName = NULL;
if (RegisterClass(&wincl) == 0) {
MessageBox(NULL,"The window class failed to register.","Error",0);
return -1;
}
}
// create window
hWindow = CreateWindow(thisClassName, "Test", WS_POPUP | WS_CLIPCHILDREN, 10, 10, 200, 200, NULL, NULL, hInst, this);
if (hWindow == NULL) {
MessageBox(NULL,"Problem creating the window.","Error",0);
return -1;
}
// show window
ShowWindow(hWindow, TRUE);
// message loop
while (GetMessage(&msg, hWindow, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// then quit window?
DestroyWindow(hWindow);
hWindow = NULL;
return msg.wParam;
}
// window proc
LRESULT CALLBACK viewvars::ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
MessageBox(NULL,"Has it gone this far?","Bench",0);
// variable
viewvars *view;
// ????
if (message == WM_NCCREATE) {
CREATESTRUCT *cs = (CREATESTRUCT*)lParam;
view = (viewvars*) cs->lpCreateParams;
SetLastError(0);
if (SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR) view) == 0) {
if (GetLastError() != 0) {
MessageBox(NULL,"There has been an error near here.","Error",0);
return FALSE;
}
}
}
else {
view = (viewvars*) GetWindowLongPtr(hwnd, GWL_USERDATA);
}
if (view) return view->WindowProc(message, wParam, lParam);
MessageBox(NULL,"If shown, the above statement did not return, and the statement below did.","Error",0);
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT viewvars::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) {
// you can access non-static members in here...
MessageBox(NULL,"Made it to window proc.","Error",0);
switch (message)
{
case WM_PAINT:
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
default:
MessageBox(NULL,"DefWindowProc Returned.","Error",0);
return DefWindowProc(hWindow, message, wParam, lParam);
break;
}
}
The message boxes appear in this order:
Has it made it this far?
Made it to window proc
DefWindowProc returned
Has it made it this far? // repeated?
Made it to window proc
DefWindowProc returned
Problem Creating the Window
Thanks for the help so far. Do you know where the problem might be?
To use a non-static class method as a window procedure requires a dynamically-allocated thunk, which is an advanced technique that I will not get into it here.
The alternative is to declare the class method as static, then it will work as a window procedure. Of course, being a static method, it can no longer access non-static class members without an instance pointer. To get that pointer, you can have the class pass its this pointer to the lpParam parameter of CreateWindow/Ex(), then the window procedure can extract that pointer from the WM_NCCREATE message and store it in the window using SetWindowLong/Ptr(GWL_USERDATA). After that, subsequent messages can retrieve that pointer using GetWindowLong/Ptr(GWL_USERDATA) and thus be able to access non-static members of that object. For example:
class viewvars
{
private:
HWND hWindow;
LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
public:
int CreateTestWindow(HINSTANCE hInst);
};
int viewvars::CreateTestWindow(HINSTANCE hInst)
{
WNDCLASS wincl;
if (!GetClassInfo(hInst, thisClassName, &wincl))
{
...
wincl.hInstance = hInst;
wincl.lpszClassName = thisClassName;
wincl.lpfnWndProc = &ThisWindowProc;
if (RegisterClass(&wincl) == 0)
return -1;
}
hWindow = CreateWindow(..., hInst, this);
if (hWindow == NULL)
return -1;
...
MSG msg;
while (GetMessage(&msg, hWindow, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DestroyWindow(hWindow);
hWindow = NULL;
return msg.wParam;
}
LRESULT CALLBACK viewvars::ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
viewvars *view;
if (message == WM_NCCREATE)
{
CREATESTRUCT *cs = (CREATESTRUCT*) lParam;
view = (viewvars*) cs->lpCreateParams;
SetLastError(0);
if (SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR) view) == 0)
{
if (GetLastError() != 0)
return FALSE;
}
}
else
{
view = (viewvars*) GetWindowLongPtr(hwnd, GWL_USERDATA);
}
if (view)
return view->WindowProc(message, wParam, lParam);
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT viewvars::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// you can access non-static members in here...
switch (message)
{
case WM_PAINT:
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWindow, message, wParam, lParam);
}
}
The main message loop must not be in your class, and especially not in a "CreateTestWindow" function, as you will not return from that function until your thread receive the WM_QUIT message that makes GetMessage returns 0.
Here is simple implementation of your viewvars class. Key points:
The Window Proc is a static member.
The link between the Window Proc and the object is made through the
use of GWLP_USERDATA. See SetWindowLongPtr.
The class DTOR destroys the window if it still exists. The WM_DESTROY
message set the HWND member to 0.
Adding OnMsgXXX methods to the class is simple: declare/define then
and just call them from the WindowProc using the 'this' pointer
stored in GWLP_USERDATA.
EDIT:
As per Mr Chen suggestion, earlier binding of the HWND to the Object (in WM_NCCREATE) to allow message handler as methods during the Window Creation.
I changed the creation styles, to show the window and to be able to move it.
// VIEWVARS.H
class viewvars {
public:
static viewvars* CreateTestWindow( HINSTANCE hInstance );
viewvars() : m_hWnd( 0 ) {}
~viewvars();
private:
static LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
static const char * m_pszClassName;
HWND m_hWnd;
};
// VIEWVARS.CPP
#include "viewvars.h"
const char * viewvars::m_pszClassName = "viewvars";
viewvars * viewvars::CreateTestWindow( HINSTANCE hInst ) {
WNDCLASS wincl;
if (!GetClassInfo(hInst, m_pszClassName, &wincl)) {
wincl.style = 0;
wincl.hInstance = hInst;
wincl.lpszClassName = m_pszClassName;
wincl.lpfnWndProc = WindowProc;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hIcon = NULL;
wincl.hCursor = NULL;
wincl.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wincl.lpszMenuName = NULL;
if (RegisterClass(&wincl) == 0) {
MessageBox(NULL,"The window class failed to register.","Error",0);
return 0;
}
}
viewvars * pviewvars = new viewvars;
HWND hWnd = CreateWindow( m_pszClassName, "Test", WS_VISIBLE | WS_OVERLAPPED, 50, 50, 200, 200, NULL, NULL, hInst, pviewvars );
if ( hWnd == NULL ) {
delete pviewvars;
MessageBox(NULL,"Problem creating the window.","Error",0);
return 0;
}
return pviewvars;
}
LRESULT CALLBACK viewvars::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
switch ( uMsg ) {
case WM_NCCREATE: {
CREATESTRUCT * pcs = (CREATESTRUCT*)lParam;
viewvars * pviewvars = (viewvars*)pcs->lpCreateParams;
pviewvars->m_hWnd = hwnd;
SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG)pcs->lpCreateParams );
return TRUE;
}
case WM_DESTROY: {
viewvars * pviewvars = (viewvars *)GetWindowLongPtr( hwnd, GWLP_USERDATA );
if ( pviewvars ) pviewvars->m_hWnd = 0;
break;
}
default:
return DefWindowProc( hwnd, uMsg, wParam, lParam );
}
return 0;
}
viewvars::~viewvars() {
if ( m_hWnd ) DestroyWindow( m_hWnd );
}
Finally, a "main" sample, but beware that there is here no way to end the process. That should be taken care by regular code (another windows).
// MAIN.CPP
#include <Windows.h>
#include "viewvars.h"
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
viewvars * pviewvars = viewvars::CreateTestWindow( hInstance );
if ( pviewvars == 0 ) return 0;
BOOL bRet;
MSG msg;
while( (bRet = GetMessage( &msg, 0, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
delete pviewvars;
return 0;
}
Unfortunately using an instance method as a C-style callback function for the WndProc won't work. At least not in any straight-forward way.
The reason it doesn't work like that is that an instance method requires the this pointer to be passed in (to point to an instance) and that won't be correctly set by the code calling the WndProc. The Win32 API was originally designed with C in mind so this is one area where you have to use some work-arounds.
One way to work around this would be to create a static method to serve as the window proc and dispatch messages to your class instances. The class instances would have to be registered (read added to a static collection) so the static method would know to dispatch WndProc messages to the instances. Instances would register themselves with the static dispatcher in the constructor and remove themselves in the destructor.
Of course all the registration and unregistration and dispatching overhead is only necessary if your WndProc handler needs to invoke other instance member functions, or access member variables. Otherwise you can just make it static and you're done.
Your window procedure is called during CreateWindow. You pass hWindow to DefWindowProc, but hWindow is only set after CreateWindow returns - so you pass DefWindowProc a garbage window handle.
I don't see a nice way to do it. You could set hWindow inside the window procedure, by changing WindowProc to:
LRESULT WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
(added the hwnd parameter), changing the call to:
return view->WindowProc(hwnd, message, wParam, lParam);
creating the window like this:
hWindow = NULL;
hWindow = CreateWindow(..., hInst, this);
if (hWindow == NULL)
return -1;
(the first assignment is to make sure hWindow is initialized; the second one is in case CreateWindow fails after calling the window procedure), and adding this at the start of WindowProc:
if(!this->hWindow)
this->hWindow = hwnd;
Step through the code in the debugger. When you get to the line
MessageBox(NULL,"DefWindowProc Returned.","Error",0);
return DefWindowProc(hWindow, message, wParam, lParam);
You will see something wrong: hWindow is garbage. You are using an uninitialized variable.