1 Developing language Choices 1.1 language selection for developing WIN32 programs C or C + +
After deciding to abandon MFC, and using the pure Win32 API to develop the Windows desktop program, there is a choice of language, that is, whether to use C + +. As a superset of C, C + + can realize all the functions that C can achieve. In fact, and vice versa, C itself can also complete the part of C + + beyond the functionality, but may require more lines of code. As far as I understand it,
- For large projects, the use of pure c to structure more secure;
- For small to medium-sized projects, C + + may be easier and faster. As the current small and medium-sized projects, so decided to use C + + as the main development language.
1.2 about the selection of C + + feature collections
After deciding to use C + +, there is a crucial choice, which is the choice of the C + + feature set. C + + is really too complex, in addition to supporting its old ancestor C all the development model, but also support based on object development (OB), Object-oriented development (OO), template technology. It can be said that C + + is a truly universal language, which also causes the high complexity of C + +. Using a different development model is equivalent to using a different programming language. As far as I am concerned, the template programming of C + + has no experience at all. Combined with past lessons and my mastery of C + +, I decided to:
- Using object-based and object-oriented two development patterns, if one function can be implemented, the preference is based on the object. The technical perspective inclined to OB comes from the experience of developing Apple object-c.
- Avoid multiple inheritance as much as possible, and this view comes from Java and. NET development experience.
- Data structures and containers, using the C + + Standard Template Library (STL), template programming itself is complex, but using STL is very easy.
2 wrapper classes for Windows window objects
For Windows desktop programs, the Concept of window and message is the core. The first thing you need to encapsulate is windows, such as MFC, which encapsulates a window object with a CWnd class. The reason why we abandoned MFC, because it is too complex to understand, so the basic Window object encapsulation must be the most simplistic.
2.1 Packaging Principles
The first principle is "simple". Can be implemented directly with a WIN32API function, never two times packaging, such as mobile window can use MoveWindow () a function implementation, the class does not appear the same function of the MoveWindow () function. MFC has a lot of this duplication of functionality, in fact, only can write less an HWND parameter, but add a layer of calls. I just want the HWND handle to appear everywhere and never hide it, because this concept is too important for windows, and developers using any encapsulation class should not ignore it.
Second, when multiple technologies of the same function can be implemented, it is preferable to choose the technology that is easy to understand, and "comprehensible" is more important than "operational efficiency".
2.2 Source Code
Header file XqWindow.h
#pragma once#include <vector>class Xqwindow{public:xqwindow (hinstance hInst); ~xqwindow ();p Rivate:hwnd HWND; Read-only, secure hinstance hinstance;public://returns the Window object handle HWND gethandle ();//Message processing. A subsequent default processing is required to return 0 and stop the message for subsequent processing, then return 1virtual int Handlemessage (HWND hwnd, UINT message, WPARAM WPARAM, LPARAM LPARAM);p rivate:/ /Raw window procedure static LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM WPARAM, LPARAM LPARAM);p rivate://registered class set static S td::vector<void*> registeredclassarray;public://Creating window void Create ();};
Implementing file XqWindow.cpp
#include "stdafx.h" #include "XqWindow.h" std::vector<void*> xqwindow::registeredclassarray;//Create window void Xqwindow::create () {wchar_t szclassname[32];wchar_t sztitle[128];void* _vptr = * ((void**) this);:: wsprintf ( Szclassname, L "%p", _vptr); Std::vector<void*>::iterator it;for (it = Registeredclassarray.begin (); it! = Registeredclassarray.end (); it++)//determines if the class of the object has registered {if ((*it) = = _vptr) break;} if (it = = Registeredclassarray.end ())//If not registered, then register {//Register window class Wndclassex wcex;wcex.cbsize = sizeof (Wndclasse X); Wcex.style = Cs_hredraw | Cs_vredraw;wcex.lpfnwndproc = Xqwindow::wndproc;wcex.cbclsextra = 0;wcex.cbwndextra = 0;wcex.hInstance = this-> Hinstance;wcex.hicon = Null;wcex.hcursor =:: LoadCursor (NULL, idc_arrow); wcex.hbrbackground = (Hbrush) (COLOR_WINDOW + 1 ); wcex.lpszmenuname = Null;wcex.lpszclassname = SZCLASSNAME;WCEX.HICONSM = null;if (0! =:: RegisterClassEx (&wcex)) Add the registered successful class to the linked list {registeredclassarray.push_back (_vptr);}} Create window if (This->hwnd = = NULL) {:: wsprintf (szTitle, L "window class name (C + + class virtual table pointer):%p", _vptr); HWND hwnd =:: CreateWindow (szclassname,sztitle,ws_overlappedwindow,0, 0, N, 600,null,null,hinstance, (LPVOID) this); if (hwnd = = NULL) {This->hwnd = null;wchar_t msg[100];::wsprintf (msg, L "CreateWindow () failed:%ld",:: GetLastError ());:: MessageBox (NULL, MSG, L "error", MB_OK); return;}}} Xqwindow::xqwindow (hinstance hInst) {This->hwnd = Null;this->hinstance = HInst;} Xqwindow::~xqwindow () {if (This->hwnd!=null &&:: IsWindow (This->hwnd))//C + + objects are destroyed before destroying the Window object {::D Estroywindow (This->hwnd); Tell system to destroy HWnd and Send Wm_destroy to Wndproc}}hwnd Xqwindow::gethandle () {return this->hwnd;} Message processing. A subsequent default processing is required to return 0 and stop the message for subsequent processing, then return 1int Xqwindow::handlemessage (HWND hwnd, UINT message, WPARAM WPARAM, LPARAM LPARAM) {return 0;} Original window procedure Lresult CALLBACK Xqwindow::wndproc (HWND hwnd, UINT message, WPARAM WPARAM, LPARAM LPARAM) {xqwindow* POBJ = Null;i F (Message = = wm_create)//When this message is received, assign the window object handle to the C + + object member while assigning the C + + object address to the Window object member {POBJ = (Xqwindow*) (((lpcreatestruct) lParam)->lpcreateparams);p Obj->hwnd = hWnd; Gets the HWND here, at which time CreateWindow () has not returned. :: SetWindowLong (HWnd, Gwl_userdata, (LONG) POBJ); Associate an HWND with a C + + object by USERDATA}pobj = (xqwindow*):: GetWindowLong (hwnd, gwl_userdata); switch (message) {case WM_CREATE: Pobj->handlemessage (hWnd, message, WParam, LParam); Break;case wm_destroy:if (pObj! = NULL)//At this time, the Window object has been destroyed by setting the hwnd= Null to notify C + + object {Pobj->hwnd = NULL;} Break;default:pobj = (xqwindow*):: GetWindowLong (hwnd, Gwl_userdata), if (pObj! = NULL) {if (Pobj->handlemessage (HWND , message, WParam, lParam) = = 0)//Call the subclass's message processing virtual function {return DefWindowProc (hWnd, message, WParam, LParam);}} Else{return DefWindowProc (hWnd, message, WParam, LParam);} break;} return 0;}
2.3 Examples of Use
The basic usage is to create a Testwindow class, inherit from Xqwindow, and then recreate the virtual function handlemessage (). All business processing code is called in Handlemessage (), since the function is a member function, all of which can be used directly to refer to the members of the Testwindow class object. An example code is as follows:
TestWindow.h
#pragma once#include "XqWindow.h" Class Testwindow:public Xqwindow{public:testwindow (HINSTANCE); ~testwindow (); Protected:int Handlemessage (HWND hwnd, UINT message, WPARAM WPARAM, LPARAM LPARAM);p rivate://Business data section int rectwidth; int rectheight;};
TestWindow.cpp
#include "stdafx.h" #include "TestWindow.h"
Testwindow::testwindow (hinstance hInst): Xqwindow (hInst) {rectwidth = $; rectheight = 200;}
Testwindow::~testwindow () {}int Testwindow::handlemessage (HWND hwnd, UINT message, WPARAM WPARAM, LPARAM LPARAM) { Paintstruct PS; HDC hdc; Switch (message) {Case WM_PAINT:HDC =:: BeginPaint (HWnd, &ps); :: Rectangle (hdc, 0, 0, this->rectwidth, this->rectheight); :: EndPaint (HWnd, &ps); return 1; Default:break; } return 0;}
Invocation part:
PTest = new Testwindow (theapp.m_hinstance);p test->create ();:: ShowWindow (Ptest->gethandle (), sw_show);:: UpdateWindow (Ptest->gethandle ());
Operating effect:
2.4 Technical Highlights
This Xqwindow class has a minimal encapsulation of window objects, mainly implementing the association of Message processing functions and C + + objects. The memory layout is as follows:
A few points to note:
(1) C + + class and one by one corresponding to the window class. Since VC + + does not enable Rtti by default, while taking into account code compatibility and operational efficiency, and does not advocate enabling RTTI, how can you differentiate all instances of the same class from other classes at run time without rtti support? Here we have used the virtual table pointer of C + +, each class with a virtual function has its own virtual table, and this virtual table pointer is stored in each instance. Different instances of the same class share a virtual table, so this gives us the opportunity to differentiate the C + + class that the object belongs to. Of course, this technique can only be used in classes with virtual functions, and there are no virtual tables for objects of classes that do not have virtual functions. For our case, the Xqwindow class has a handlemessage virtual function, so all other subclasses that inherit this class have their own virtual tables.
Before RegisterClass (), first determine whether the virtual table pointer of the class that the current C + + object belongs to exists in the Vptraraay list. If not, the window class is registered and the virtual table pointer is stored in the Vptrarray linked list, and if present, the window class corresponding to the virtual table pointer is used directly.
It is important to note that the operation to get the object virtual table pointer value cannot be done in the Xqwindow::xqwindow () constructor, because the virtual table pointer member of the C + + object has not been set to the virtual table address of the derived class (because the constructor for the subclass has not been called) when executing this function. Therefore, the virtual table pointer value must be obtained after the object construction is complete, which is why the Create () cannot be called in the Xqwindow () constructor. (I used to put create () into Xqwindow () in order to simplify the call, causing all objects to have the same result as the virtual table pointer)! )
(2) The relationship between the C + + object and the Window object. After a C + + object is created, calling Create () is the only way to bind the Window object together. The C + + object can no longer create a new window until the old window is destroyed, and it is useless to call create () multiple times.
The life expectancy of C + + objects is also greater than the corresponding window life, otherwise the use of C + + objects during Windows will cause illegal access to memory problems. The life sequence of these two objects is: C + + object Birth--Call Create () to produce a Window object--For some reason the window object destroys the--c++ object.
To prevent C + + objects from being destroyed before the Window object, in the Xqwindow class's destructor, the Window object is first destroyed by DestroyWindow (). When the Window object is destroyed, the HWND of the C + + object is also set to NULL to notify the destruction of the C + + object window.
Image point of view: C + + object and Window object is monogamous, and only widows can not divorce under the condition of marital relationship, and C + + object is a Long life party, Window object is short-lived side. A C + + object can regenerate a new window only after a Window object has died. And before the C + + object dies, the window object needs to be killed and buried.
(3) reference to each other of C + + objects and window objects. A C + + object references a Window object through a member variable hwnd, while a window object points to a C + + object by gwl_userdata additional data blocks. In addition, in order to capture wm_crate messages in a timely manner and process them in handlemessage, the assignment of the C + + member HWND is not after CreateWindow (), but rather when the original window procedure function processes the wm_creat message. This is mainly related to the CreateWindow () principle.
CreateWindow ()
{
HWND hwnd = malloc (..);
Initializes the window object;
WndProc (hwnd, Wm_crate,..); The window is now created
Other operations;
return hwnd;
}
Similarly, the principle of DestroyWindow () is.
DestroyWindow (HWND)
{
Window object cleanup work;
WndProc (hwnd, Wm_destroy,..); The window is not visible at this time.
other operations;
Free (HWND);
}
2.5 Existing problems
Although the Xqwindow class can work well, there are some problems:
(1) Because the Window object references C + + objects by USERDATA, the program will crash if other code modifies the data block through SetWindowLong (hwnd, gwl_userdata, XXX). How to prevent this kind of destruction, need further study.
(2) Using the virtual table pointer of C + + object, and the specific memory layout of this pointer does not have a clear specification standard, once the VC + + compiler modifies the virtual table pointer storage location, the program will be problematic. However, due to binary compatibility, VC + + is unlikely to make such a change.
31 points to feel
Xqwindow class of source code altogether less than 150 lines, but took me 2 days of spare time to complete. This involves an in-depth understanding of C + + object memory layout, window creation, destruction, and message processing. Writing a small class is so hard to write a robust class library is very difficult to think of MFC is really not easy.
Regarding this kind, everybody has the good idea, welcome the exchange discussion.
Relive the WIN32 API------The simplest Windows window wrapper class