[英]Win32 C++ Create a Window and Procedure Within a Class
前置/問題
我正在嘗試制作一個相當簡單的工具來幫助調試變量值。 因為它是完全自我包含的類是我的目標。 最終產品我可以使用類中的函數,如ShowThisValue(無論如何)。
我遇到的問題是,如果可能的話,我無法弄清楚在課堂上有這個程序。 這是短版本,有問題。
-Code再次更新11/29 / 13-我現在把它放在自己的項目中。
[main.cpp中]
viewvars TEST; // global
TEST.CreateTestWindow(hThisInstance); // in WinMain() right before ShowWindow(hwnd, nFunsterStil);
[viewvars.h]整個更新
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;
}
}
消息框按此順序顯示:
感謝你目前的幫助。 你知道問題出在哪里嗎?
要使用非靜態類方法作為窗口過程,需要動態分配的thunk,這是一種高級技術,我不會在這里介紹它。
另一種方法是將類方法聲明為static
,然后它將作為窗口過程。 當然,作為一個static
方法,它不再能夠在沒有實例指針的情況下訪問非靜態類成員。 要獲取該指針,您可以讓該類將其this
指針傳遞給CreateWindow/Ex()
的lpParam
參數,然后窗口過程可以從WM_NCCREATE
消息中提取該指針並使用SetWindowLong/Ptr(GWL_USERDATA)
將其存儲在窗口中。 之后,后續消息可以使用GetWindowLong/Ptr(GWL_USERDATA)
檢索該指針,從而能夠訪問該對象的非靜態成員。 例如:
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);
}
}
主消息循環不能在你的類中,特別是在“CreateTestWindow”函數中,因為在線程收到使GetMessage
返回0的WM_QUIT
消息之前,你不會從該函數返回。
這是viewvars
類的簡單實現。 關鍵點:
編輯:
我更改了創建樣式,以顯示窗口並能夠移動它。
// 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 );
}
最后,一個“主要”樣本,但要注意,這里沒有辦法結束這個過程。 這應該由常規代碼(另一個窗口)來處理。
// 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;
}
不幸的是,使用實例方法作為WndProc的C風格回調函數將不起作用。 至少不是直截了當的。
它不起作用的原因是實例方法需要傳入this
指針(指向一個實例),並且調用WndProc的代碼不能正確設置它。 Win32 API最初設計時考慮到了C,因此這是一個必須使用一些解決方法的領域。
解決此問題的一種方法是創建一個靜態方法作為窗口proc並將消息分派給類實例。 必須注冊類實例(讀取添加到靜態集合),以便靜態方法知道將WndProc消息分派給實例。 實例將在構造函數中使用靜態調度程序注冊自己,並在析構函數中刪除它們自己。
當然,只有當您的WndProc處理程序需要調用其他實例成員函數或訪問成員變量時,才需要所有注冊和取消注冊和調度開銷。 否則你可以讓它靜止,你就完成了。
在CreateWindow期間調用您的窗口過程。 您將hWindow傳遞給DefWindowProc,但hWindow僅在CreateWindow返回后設置 - 因此您將DefWindowProc傳遞給垃圾窗口句柄。
我沒有看到一個很好的方法來做到這一點。 您可以通過將WindowProc更改為:在窗口過程中設置hWindow:
LRESULT WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
(添加了hwnd參數),將調用更改為:
return view->WindowProc(hwnd, message, wParam, lParam);
像這樣創建窗口:
hWindow = NULL;
hWindow = CreateWindow(..., hInst, this);
if (hWindow == NULL)
return -1;
(第一個賦值是確保hWindow被初始化;第二個是在調用窗口過程后CreateWindow失敗的情況下),並在WindowProc的開頭添加它:
if(!this->hWindow)
this->hWindow = hwnd;
逐步調試調試器中的代碼。 當你到達線
MessageBox(NULL,"DefWindowProc Returned.","Error",0);
return DefWindowProc(hWindow, message, wParam, lParam);
你會看到一些錯誤: hWindow
是垃圾。 您正在使用未初始化的變量。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.