-
Prototype Elimination!!
I REALLY hate using prototypes. Is it possible to never use them? C# does not use them at all, I don't think they are at all necessary in C++ either.
I have a Form class which is a window. I cannot process the window messages from the class. I have to use a global WndProc function and then pass the variables into a new class.
Here is the problem, the form class needs to know about the global WndProc function and the WndProc function needs to know about the class.
So I had to put a prototype of the WndProc function above the class. I want that nonsense to stop. I did not have to do that in Borland. Is this a compiler issue?
-
No, it's not. Not a specific compiler at least.
C compilers basically are top-down compilers. They read and use line by line of the source. C# and Java compilers are lookahead compilers.
The difference is that if a C compiler encounters a symbol it doesn't know, it immediatly fails. A C# compiler first checks the rest of the source file and even some other source files if it can find the symbol being declared somewhere. Only if that too is useless, it fails.
C++ compilers are hybrids. Usually they still show the same behavior as a C compiler, but in some cases they don't.
The reason why C works that way is that C was originally intended as a minimal language. The programs it creates are fast and small. This is to accommodate weak computers with little storage space. And the compiler should do the same. Top-down compilers are easier to implement than the other sort. Thus the requirement of function prototypes.
It's possible to write a C/C++ program completly without prototypes, but only if it's limited to a single source file and you don't have any round-calling functions (a calls b, b calls a, or similar things with more functions).
For your specific problem, I recommend two changes.
First, your class declaration probably doesn't need to know about the global WndProc, so you can simpy define the WndProc above the class implementation.
Second, screw the global function altogether. Declare it as static private member of the class. Makes much more sense that way.
Finally, I have no idea why you don't like prototypes. I think they are a great self-documentation of source code.
-
They would be ok if I did not make changes the the source code. I just don't like making 2 changes instead of one.
About the static WndProc, the downside to using a static WndProc is that it cannot access the instance members of the class. And I need access to them.
-
Neither can the global. Here's how I do it (from a current project of mine):
Code:
// Header File:
class Application
{
int app_main(HINSTANCE instance, tchar_t *cmdline, int showcmd);
static LRESULT CALLBACK WndProcStub(HWND hwnd, UINT msg, WPARAM w, LPARAM l);
protected:
HINSTANCE app_instance;
HWND app_hwnd;
virtual LRESULT WndProc(HWND hwnd, UINT msg, WPARAM w, LPARAM l);
// ...
};
// Implementation
int Application::app_main(HINSTANCE instance, tchar_t *cmdline, int showcmd)
{
app_instance = instance;
// Create a window etc...
::WNDCLASS wc;
// ...
wc.lpszClassName = _T("T3D Game Window Class");
::RegisterClass(&wc);
app_hwnd = CreateWindow(_T("T3D Game Window Class"),
_T("T3D Game"), get_window_flags(), CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL,
NULL, instance, static_cast<void *>(this));
::ShowWindow(hwnd_app, showcmd);
::UpdateWindow(hwnd_app);
// Enter message loop
MSG msg;
while(::GetMessage(&msg, NULL, 0, 0) > 0)
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return static_cast<int>(msg.wParam);
}
LRESULT CALLBACK Application::WndProcStub(HWND hwnd, UINT msg, WPARAM w, LPARAM l)
{
if(msg == WM_NCCREATE)
{
::CREATESTRUCT *pcs = reinterpret_cast<::CREATESTRUCT *>(l);
::SetWindowLongPtr(hwnd, 0,
reinterpret_cast<LONG_PTR>(pcs->lpCreateParams));
}
Application *pthis = reinterpret_cast<Application *>(
::GetWindowLongPtr(hwnd, 0);
return pthis->WndProc(hwnd, msg, w, l);
}
// etc...
-
As for code changes - yeah, I don't like that part either, but you could see it as punishment for not planning properly ;)
-
This will not compile:
::CREATESTRUCT *pcs= reinterpret_cast<::CREATESTRUCT*>(lParam);
Error: Parse error before ;
This will but you know better than I do, CRASH!
::CREATESTRUCT *pcs;
The compiler does not like something here:
= reinterpret_cast<::CREATESTRUCT*>(lParam);
-
It did not like the :: before reinterpret_cast<CREATESTRUCT*>.
Code:
static LRESULT CALLBACK WndProcStatic(HWND h, UINT m, WPARAM wParam, LPARAM lParam)
{
switch(m)
{
case WM_NCCREATE:
{
ostringstream oss;
::CREATESTRUCT *pcs= reinterpret_cast<CREATESTRUCT*>(lParam);
oss<< pcs<<" "<< lParam<<" "<<
::SetWindowLongPtr(h, 0, reinterpret_cast<LONG_PTR>(pcs->lpCreateParams));
MessageBox(NULL, oss.str().c_str(), "", MB_OK);
}
default:
{
Form *pthis= reinterpret_cast<Form*>(::GetWindowLongPtr(h, 0));
ostringstream oss;
oss<< pthis<<" "<<" "<<GetWindowLongPtr(h, 0);
if(!pthis)
{
MessageBox(NULL, oss.str().c_str(), "", MB_OK);
return 0;
}
return pthis->WndProc(h, m, wParam, lParam);
}
}
The program produces a fatal error. Is ::SetWindowLongPtr supposed to return 0? Because it does. ::GetWindowLongPtr also returns 0.
First default is called then WM_NCCREATE, and then default again, then the program crashes in default trying to use a pointer that points to nothing. That is if I did not stop it by returning 0 before if got to: return pthis->WndProc(h, m, wParam, lParam);
-
I am using the extended window style so I changed the 0's in GetWindow and SetWindow to GWL_EXSTYLE and now none of the messageboxe's show indicating that the variables are initialized but I still get the same error when it tries to return the WndProc.
Or maybe it is because my createparameters are 0:
pcs->lpCreateParams shows 0. If that is it, how do I change it?
-
Got it working. Your code confused me. It looks like you wanted the part I have in default not return the DefWindowProc(). So what I tried was putting all that code in WM_NCCREATE and it works.
-
I haven't tested the code yet ;)
And the :: is because my code is in a namespace.
-
I never use a default: in my WndProcs. I like my way better.
Which works like this:
Code:
WndProc
{
switch(msg)
{
case WM_SOME:
// Handled
return 0;
case WM_OTHER:
// Handled, but we still want default action
break;
}
// Default action
return DefWindowProc();
}
The reason why my code doesn't contain a DefWindowProc call is that this call is in the true WndProc.
-
Every time the program starts, the pthis pointer is not initialized right away so I always get my messagebox error. I don't think that should be the case. What did I do wrong?
Code:
case WM_NCCREATE:
{
::CREATESTRUCT *pcs= reinterpret_cast<CREATESTRUCT*>(lParam);
::SetWindowLongPtr(h, 0, reinterpret_cast<LONG_PTR>(pcs->lpCreateParams));
Form *pthis= reinterpret_cast<Form*>(::GetWindowLongPtr(h, 0));
if(!pthis)
{
MessageBox(NULL, "create", "Error", MB_OK);
//return -1;
//return 0;
}
else
return pthis->WndProc(h, m, wParam, lParam);
}
-
Check if WM_NCCREATE is really the first message the WndProc receives. If not, I need to change my code (and you too ;))
-
No it's 36, whatever that is.
-
I tried using 36 and also WM_CREATE but both do the same:
Code:
static LRESULT CALLBACK WndProcStatic(HWND h, UINT m, WPARAM wParam, LPARAM lParam)
{
ostringstream oss;
oss<< m;
//MessageBox(NULL, oss.str().c_str(), "Error", MB_OK);
switch(m)
{
case 36:
{
::CREATESTRUCT *pcs= reinterpret_cast<CREATESTRUCT*>(lParam);
::SetWindowLongPtr(h, 0, reinterpret_cast<LONG_PTR>(pcs->lpCreateParams));
Form *pthis= reinterpret_cast<Form*>(::GetWindowLongPtr(h, 0));
if(!pthis)
{
MessageBox(NULL, "create", "Error", MB_OK);
}
else
return pthis->WndProc(h, m, wParam, lParam);
break;
}
}
return DefWindowProc(h, m, wParam, lParam);
}
-
It works without most of that code too for some reason:
::CREATESTRUCT *pcs= reinterpret_cast<CREATESTRUCT*>(lParam);
::SetWindowLongPtr(h, 0, reinterpret_cast<LONG_PTR>(pcs->lpCreateParams));
That is all that is in NCCREATE and it works.
Should that be ok?
-
Uh... what?
Isn't that what I had all along?
-
Here is what you had:
Code:
LRESULT CALLBACK Application::WndProcStub(HWND hwnd, UINT msg, WPARAM w, LPARAM l)
{
if(msg == WM_NCCREATE)
{
::CREATESTRUCT *pcs = reinterpret_cast<::CREATESTRUCT *>(l);
::SetWindowLongPtr(hwnd, 0,
reinterpret_cast<LONG_PTR>(pcs->lpCreateParams));
}
Application *pthis = reinterpret_cast<Application *>(
::GetWindowLongPtr(hwnd, 0);
return pthis->WndProc(hwnd, msg, w, l);
}
-
36 would be WM_GETMINMAXINFO...
-
This should never crash. And I missed a closing parenthesis.
Code:
LRESULT CALLBACK Application::WndProcStub(HWND hwnd, UINT msg, WPARAM w, LPARAM l)
{
if(msg == WM_NCCREATE)
{
::CREATESTRUCT *pcs = reinterpret_cast<::CREATESTRUCT *>(l);
::SetWindowLongPtr(hwnd, 0,
reinterpret_cast<LONG_PTR>(pcs->lpCreateParams));
}
Application *pthis = reinterpret_cast<Application *>(
::GetWindowLongPtr(hwnd, 0));
if(pthis)
return pthis->WndProc(hwnd, msg, w, l);
else
return ::DefWindowProc(hwnd, msg, w, l);
}
-
Guess what? Your not going to like this, it does not work. I did a little experiment and took everything out of WM_NCCREATE and I got the same results- the program seemed to work correctly.
I came to find out that the Window process that the instance form's process was calling was the static one and not the one it should be calling.
Something must not be working right with this:
::CREATESTRUCT *pcs= reinterpret_cast<CREATESTRUCT*>(lParam);
::SetWindowLongPtr(h, 0, reinterpret_cast<LONG_PTR>(pcs->lpCreateParams));
-
Here is where I got the idea to use a global WndProc:
http://www.rpi.edu/~pudeyo/articles/wndproc/
He does not mention anything like this. Maybe it can't work?
Look that over and tell me what you think.
-
This one is the most like yours, although he still talks about using a global WndProc and not a static. But I think static should work the same way.
"Per-Window Data"
-
Static and global are binary equivalent.
-
Have you tried your code out yo see if you can get it working? I could not get either to work.
-
No, not yet. And I will not for some days, I'll not be at home.
-
Ok. If I figure out how to get this working i'll post back, if not i'll just be looking forward to whatever you come up with.