|
||||||||||||||||||
Add Forum to Favorites | Send Topic To a Friend | View Forum FAQ | Track this topic Page: 1 2 3 »» |
Last Thread Next Thread ![]() |
| Creating a Win32 Window Wrapper Class |
|
![]() Oluseyi Staff Member since: 5/14/2001 From: New York, NY, United States |
||||
|
|
||||
| Typo, page 2: Instead of typedef std::map>long, tyMessageHandler< tyMessageMap, it should be typedef std::map<long, tyMessageHandler> tyMessageMap. Oh, and comments on the architecture are welcome as well. [ GDNet Start Here | GDNet Search Tool | GDNet FAQ ] [ MS RTFM [MSDN] | SGI STL Docs | Boost ] [ Google! | Asking Smart Questions | Jargon File ] Thanks to Kylotan for the idea! |
||||
|
||||
![]() Ecko Member since: 9/7/2000 From: Etiwanda, USA!!!!!!! |
||||
|
|
||||
| This is well layed out, but some suggestions or whatever you can call them. one thing I didn't like is that you have to pass hInstance try out this: HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL); the second thing is that the peek message and show window and all that could really be automated, show window should automatically be shown when the window is created. Then if you want the option of hiding your window just write a show function with a boolean value in it. ----------------------------------------------------------- "People who usualy use the word pedantic usualy are pedantic!"-me |
||||
|
||||
![]() Oluseyi Staff Member since: 5/14/2001 From: New York, NY, United States |
||||
|
|
||||
quote: I was lazy (and have been ever since; I don't think I've looked inside that file in months - some things in the article were surprising to even me!) Good point, though. quote: Currently, the class defaults to using GetMessage (enough for most apps), but can be instructed to use PeekMessage when you have high performance apps that can't afford the execution thread blocking while waiting for some Windows message. Flexibility comes at the price of ease, but it has "automatic" defaults. Using PeekMessage and WaitMessage is kinda silly, though... quote: That wouldn't be expected behavior. Calling Create shouldn't cause a display. Feel free to modify your versions, though. Thanks for the input. [ GDNet Start Here | GDNet Search Tool | GDNet FAQ ] [ MS RTFM [MSDN] | SGI STL Docs | Boost ] [ Google! | Asking Smart Questions | Jargon File ] Thanks to Kylotan for the idea! |
||||
|
||||
![]() Red Ghost Member since: 4/18/2002 From: Paris, France |
||||
|
|
||||
| Just curious: were you inspired by www.relisoft.com/win32/generic.html when designing your own encapsulation ? Ghostly yours, Red. |
||||
|
||||
![]() Ironside Member since: 10/12/2000 From: Bothell, USA |
||||
|
|
||||
| I doubt it, Win32 wrappers are pretty common. Most people write the basic WIN32 functions once or twice, get really sick of re-implementing them and toss them in a wrapper. |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| Nice article, just two points... On the first page with your first example, your window destructor will not get called when it goes out of scope because it doesn't go out of scope at all (the pointer does). You need to explicitly delete it in that case or place your window on the stack so it does go out of scope. Similarly, your final example has the same memory leak. g_wnd will not get destructed because you are not deleting it anywhere. Luck, Fedar |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| Two things, First... delete a window clas is no a good idea, it will crash in many systems... Second, what about registering class prevoiusly in the winmain? If i wanna do this i have one prob, the class needs one field lpfnWndProc, that points to the mainwndproc, but it doesnot accept a method of a clas, only a plain proc (like the Win32 threads) There is a way to solve this? Txs! |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| > First... delete a window clas is no a good idea, it will > crash in many systems... This is his own user type, CWindow, not some Win32 API class that you testify crashes on many systems if you delete it. It's just a simple memory leak. |
||||
|
||||
![]() Dactylos Member since: 5/27/2001 From: AC, Sweden |
||||
|
|
||||
quote: You could extend the wrapper with an overloaded CWindow::Create method that takes a WNDCLASS(EX) structure as a parameter and fills in the lpfnWndProc field before registering and using that WNDCLASS. That way all you have to do is fill in a custom WNDCLASS(EX) struct and then let the wrapper handle the rest... BTW, Oluseyi, nice article. [edited by - Dactylos on April 30, 2002 8:37:51 AM] |
||||
|
||||
![]() tcpo Member since: 4/22/2002 |
||||
|
|
||||
| I have a question. In Window::HandleMessages(), IsExit() is used to check the exit status set by OnDestroy(). Because OnDestroy calls PostQuitMessage, I've tried to replace IsExit() is HandleMessage() with "if (msg.message == WM_QUIT)", but then my program fails to stop. Would that be my silly programming mistake? Or have i misunderstood anything here? Thanks. |
||||
|
||||
![]() SnprBoB86 Member since: 5/29/2001 |
||||
|
|
||||
| very cool, www.relisoft.com also has a pretty great windows wrapper creation tutorial. I recomend anyone who wishes to develope their own should check that out as well. -SniperBoB- |
||||
|
||||
![]() Oluseyi Staff Member since: 5/14/2001 From: New York, NY, United States |
||||
|
|
||||
quote: No, I've actually never seen that wrapper. I'll have a looksee now. quote: Good point. I generally initialize my windows in my own code using references, like so: Window & MainWin = Window( hInstance ); That automatically invokes the destructor when it goes out of scope. Also, using smart pointers to encapsulate dynamically allocated memory gives me less reason to even write destructors these days! quote: My own class has a static method called Init along with a static boolean variable m_Initialized that are referenced in Window::Create: if( !m_Initialized ) Init(); This uses a default WNDCLASSEX structure, but you could easily modify the architecture to take a WNDCLASSEX structure as a parameter (it would have a few interesting design problems though). As for the "plain proc", see my static (and private) MsgRouter method. Since it's static, it uses the __stdcall calling convention (and is therefore acceptable to the lpfnWndProc pointer). It extracts a pointer to the Window object using GetWindowLong and then calls the appropriate message handler for that object - obtained via GetMessageHandler. quote: Your message pump never gets the WM_QUIT message, which is why I have IsExit. OnDestroy calls SetExit (I have problems with Windows message pumps and proper termination when using PeekMessage, so that's kind of a workaround) and HandleMessages calls IsExit (which is inline, so don't worry about overhead). I've put the window wrapper class along with some other files that go together (part of my engine-in-progress) on some school webspace I have. Get them here. The Window class changed a little bit - HandleMessages was renamed to Dispatch, and there might be a few other differences here and there. Take a look at the complete sources to see exactly what I did (there's also a little DXGraphics wrapper - incomplete - in there, and a basic scaffold for wrapping DXAudio). Thanks to everyone for the positive comments and feedback. Can I get some design critiques too? [Edit: UNIX URLs are case-sensitive.] [ GDNet Start Here | GDNet Search Tool | GDNet FAQ ] [ MS RTFM [MSDN] | SGI STL Docs | Boost ] [ Google! | Asking Smart Questions | Jargon File ] Thanks to Kylotan for the idea! [edited by - Oluseyi on April 30, 2002 3:24:38 PM] |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| is it just me, or does this wrapper seem more complicating as if you would just do it the normal way? |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
quote: If you were a real developer you would either use MFC if you must use the Win32 API (horrible idea if you like standard C++, but writing your own wrappers really is not practical) or use someone elses GUI library (like Trolltech's Qt which also has the advantage of being crossplatform). Nice article besides though. |
||||
|
||||
![]() Oluseyi Staff Member since: 5/14/2001 From: New York, NY, United States |
||||
|
|
||||
quote: For very small apps, yes. As with all things involving wrappers, there is a threshold below which investing in this kind of framework is not practical. Goes to show how much Windows does in the background, though... quote: MFC is large and bloated. I prefer using WTL if I must use a complete Win32 API wrapper. This class only wraps windows, which is often very useful for DirectX applications and other "scaffolding-only" projects. Qt is pretty messy too (unless it's been significantly revised since I last looked into it [2.2.x]). moc is as bad as MFC/WTL's AFX_MESSAGE_MAP macros (actually, I think it's worse). The signals and slots mechanism is cool, but I find its implementation inelegant given the possibilities within the language. Yes, preprocessing macros to generate intermediate code probably results in faster code than using a runtime structure, but these applications don't need the fractional performance gains. While I admit that I've never found a use for runtime message handler switching, it's nice to have the option should it ever arise. No other available option I have come across provides that. That said, Qt is an impressive piece of work, quite modern and (IMO) better designed and implemented than MFC. quote: Thanks. [ GDNet Start Here | GDNet Search Tool | GDNet FAQ ] [ MS RTFM [MSDN] | SGI STL Docs | Boost ] [ Google! | Asking Smart Questions | Jargon File ] Thanks to Kylotan for the idea! |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
There is an error in this code: tyMessageIterator Window::GetMessageHandler(long message) { // m_MsgHandlers is a tyMessageMap instance tyMessageIterator it = m_MsgHandlers.find(message); if(it = m_MsgHandlers.end()) return NULL; return it; } |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| I agree with the complication comment... generally I develop my code to be cross-platform as possible (using libraries like STL, SDL, GLUT, HawkNL, etc) and keeping the message-map OS-specific code to a minimum. It appears to me that writing this kind of code can take a lot more time than writing "C+" code (i.e. "mostly C with features from C++ that increase, not decrease, development time). If you keep a general template C/CPP file on your harddrive with ALL the message-mapping that you require for a basic window, with all the message-pump functionality intact, you generally don't have to write a lot of class wrapper code.
/////////////////////////////////////////////////
// Headers
#include <windows.h>
#include "CEngine.h"
/////////////////////////////////////////////////
// Globals
CEngine* g_lpEngine = NULL; // Yes! Globals! Bring on the flames!
/////////////////////////////////////////////////
// WinProc
int WinProc(...)
{
switch (msg)
{
case WM_ACTIVATE:
{
g_lpEngine->OnActivate(/*params*/);
} break;
case WM_SOCKET:
{
g_lpEngine->OnSocket(/*params*/);
} break;
/*and so on, ad infinitum*/
}
return 0;
}
/////////////////////////////////////////////////
int WINAPI WinMain(...)
{
lpEngine = new CEngine(/*params*/);
/*and so on, ad infinitum*/
}
/////////////////////////////////////////////////
Simple, to the point. Easy to debug. And the way it's done, it's easier to map events across different platforms (if you are porting code to X Windows, MacOS, and so on). Maybe it's personal preference, but I never understood the point of wrapping in C++ classes any parts of code that do not have high amounts of implementation in a single program. (Kinda like writing a C++ class wrapper specifically for anything that is only done once in a program). It seems this tutorial might be more directed toward programmers migrating from Java to C++, what with the exception handling, program-encompassing class, et cetera. KISS rule -- Keep It Simple, Stupid! |
||||
|
||||
![]() Buster Member since: 4/26/2000 From: USA |
||||
|
|
||||
| It seems like you just re-wrote your own version of MFC. |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| Barely... keep in mind that what I'm talking about is designed with one idea in mind:keep the OS-specific stuff as simple as possible. Of course a game engine might have class methods in it, and of course they must map to OS events inside the OS event handler. But think outside the Windows box... every OS has its event handler, and it's sometimes easier to develop a program if you abstract the actual application engine from the OS-specific stuff. That is why I mapped WM_ACTIVATE for example to Engine->OnActivate()... every language has an entry-point for its message loop (Java with init(), Mac Carbon with main(), and so on). Find the commonalities between languages/OS's, and design the application as such. |
||||
|
||||
![]() DerekSaw Member since: 11/26/1999 From: 5-Claw, Dragonland |
||||
|
|
||||
| Isn't that the 1st msg to be sent by Windows is WM_GETMINMAXINFO instead of WM_NCCREATE? One thing to note, whenever exception occur in the same scope of Window(), problem come. eg. I put a throw "Rubbish";just after the line wnd.Show() ... it got access violation. Maybe the sample code presented is not fully functional yet... waiting for your part 2.... anyway... good work. I like the idea of using std::map. |
||||
|
||||
![]() Oluseyi Staff Member since: 5/14/2001 From: New York, NY, United States |
||||
|
|
||||
quote: Probably; I really don't know and can't be arsed to look it up right now. quote: You threw a string rather than an exception, which is what caused the access violation. Try replacing that with throw exception( "Rubbish" ); A few posts up I provided a link to the complete source. [ GDNet Start Here | GDNet Search Tool | GDNet FAQ ] [ MS RTFM [MSDN] | SGI STL Docs | Boost ] [ Google! | Asking Smart Questions | Jargon File ] Thanks to Kylotan for the idea! |
||||
|
||||
![]() DerekSaw Member since: 11/26/1999 From: 5-Claw, Dragonland |
||||
|
|
||||
| ... the funny thing is, the access violation happens inside the std::map (have the debug to stop on all exception)... and the only place that uses map is router. hmmm... windows programming is unpredictable. After adding some code in the ~Window() and rearranging few lines in the router, the error gone. I suppose it is not the throw "Rubbish";problem. |
||||
|
||||
![]() DerekSaw Member since: 11/26/1999 From: 5-Claw, Dragonland |
||||
|
|
||||
I tried throwing other than "straight string", this time the std::runtime_error, but still the same - Access Violation.
#include "window.h"
using namespace Obsidian;
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE, LPSTR cmd, int show)
{
try
{
Window wnd(hinst);
wnd.Create();
wnd.ShowWindow(show);
throw std::runtime_error(std::string("Rubbish"));
while(wnd.Dispatch())
;
return wnd.GetExitCode();
}
catch(std::runtime_error & ex)
{
::MessageBox(NULL, ex.what(), "Exception", MB_OK | MB_TASKMODAL);
}
catch(...)
{
}
return 0;
}
Anyway, how you all have that syntax-highlighted listbox? |
||||
|
||||
![]() Dactylos Member since: 5/27/2001 From: AC, Sweden |
||||
|
|
||||
quote: Read the FAQ. |
||||
|
||||
|
Page: 1 2 3 »» All times are ET (US) ![]() |
Last Thread Next Thread ![]() |
|