I've started Direct2D from a very simple example.
Acquire the factory and ID2D1HwndRenderTarget, then handle WM_PAINT message to draw just a background with a solid color using "Clear" function.
It's work fine, until I start to move the window. When the window is moving it turns gray like nothing is drawing. I've tried to draw an ellipse, and the result is the same.
How could one present the window content with the window moving?
P.S. In case the code is needed
#include <Windows.h>
#include <d2d1_1.h>
#pragma comment(lib,"d2d1")
ID2D1Factory * d2factory_ptr = NULL;
ID2D1HwndRenderTarget * renderTarget_ptr = NULL;
LRESULT CALLBACK mainWinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(
HINSTANCE hInstance
, HINSTANCE prevInstance
, LPWSTR cmd
, int nCmdShow
) {
WNDCLASSEX wndClassStruct;
ZeroMemory(&wndClassStruct, sizeof(WNDCLASSEX));
wndClassStruct.cbSize = sizeof(WNDCLASSEX);
wndClassStruct.hbrBackground = (HBRUSH)COLOR_WINDOW;
wndClassStruct.style = CS_HREDRAW | CS_VREDRAW;
wndClassStruct.hInstance = hInstance;
wndClassStruct.lpfnWndProc = mainWinProc;
wndClassStruct.lpszClassName = TEXT("MainWnd");
RegisterClassEx(&wndClassStruct);
RECT windowRect = { 0,0,640,480};
AdjustWindowRectEx(&windowRect, WS_OVERLAPPEDWINDOW, 0, WS_EX_APPWINDOW);
HWND hWnd = CreateWindowEx(WS_EX_APPWINDOW, TEXT("MainWnd"), TEXT("Direct 2D Test Window"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE, CW_USEDEFAULT, 0, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, NULL, NULL, hInstance, 0);
{
D2D1_FACTORY_OPTIONS fo;
ZeroMemory(&fo, sizeof(D2D1_FACTORY_OPTIONS));
IID const factoryIID = IID_ID2D1Factory1;
HRESULT res = S_OK;
if (S_OK != (res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factoryIID, &fo, &d2factory_ptr))) {
return 0;
}
RECT clientRect;
GetClientRect(hWnd, &clientRect);
D2D1_RENDER_TARGET_PROPERTIES renderTargetProps;
ZeroMemory(&renderTargetProps, sizeof(D2D1_RENDER_TARGET_PROPERTIES));
renderTargetProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
renderTargetProps.pixelFormat = (D2D1_PIXEL_FORMAT) { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED };
renderTargetProps.dpiX = 0;
renderTargetProps.dpiY = 0;
renderTargetProps.usage = D2D1_RENDER_TARGET_USAGE_FORCE_BITMAP_REMOTING;
renderTargetProps.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
D2D1_HWND_RENDER_TARGET_PROPERTIES hwndRenderProps;
ZeroMemory(&hwndRenderProps, sizeof(D2D1_HWND_RENDER_TARGET_PROPERTIES));
hwndRenderProps.hwnd = hWnd;
hwndRenderProps.pixelSize = (D2D1_SIZE_U) { clientRect.right - clientRect.left, clientRect.bottom - clientRect.top };
hwndRenderProps.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
if (S_OK != (res = d2factory_ptr->lpVtbl->CreateHwndRenderTarget(d2factory_ptr, &renderTargetProps, &hwndRenderProps, &renderTarget_ptr))) {
return 0;
}
}
ShowWindow(hWnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DestroyWindow(hWnd);
if(NULL != renderTarget_ptr)
renderTarget_ptr->lpVtbl->Base.Base.Base.Release((IUnknown*)renderTarget_ptr);
if (NULL != d2factory_ptr)
d2factory_ptr->lpVtbl->Base.Release((IUnknown*)d2factory_ptr);
return 0;
}
LRESULT onPaintMainWindow() {
ID2D1RenderTargetVtbl renderTargetFuncs = renderTarget_ptr->lpVtbl->Base;
ID2D1RenderTarget * This = (ID2D1RenderTarget*)renderTarget_ptr;
D2D1_TAG tag1, tag2;
D2D1_COLOR_F backgroundClr = (D2D1_COLOR_F) { 0.0, 0.5, 1.0, 1.0 };
renderTargetFuncs.BeginDraw(This);
renderTargetFuncs.Clear(This, &backgroundClr);
renderTargetFuncs.EndDraw(This, &tag1, &tag2);
return 0;
}
LRESULT CALLBACK mainWinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (WM_PAINT == uMsg)
return onPaintMainWindow();
if (WM_DESTROY == uMsg) {
PostQuitMessage(0); return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Configure your WNDCLASSEX to not have a background brush.
Replace this line:
wndClassStruct.hbrBackground = (HBRUSH)COLOR_WINDOW;
With this:
wndClassStruct.hbrBackground = GetStockObject(NULL_BRUSH);
Alternatively, you can modify mainWndProc to swallow the WM_ERASEBKGND messages. It achieves the same effect by not allowing the window to erase itself before issuing a WM_PAINT.
LRESULT CALLBACK mainWinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (WM_PAINT == uMsg)
return onPaintMainWindow();
if (uMsg == WM_ERASEBKGND)
{
// ignore requests to erase the background since the wm_paint
// handler is going to redraw the entire window.
return 0;
}
if (WM_DESTROY == uMsg) {
PostQuitMessage(0); return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Related
I saw an article on the Microsoft website MSDN that introduced an advanced way of writing.
I apply it to the extension of the class, and usually I don't have a problem.
But recently I encountered a problem when I tried to write a class.
I want to implement a listview class to provide us some convenience when we manipulate the listview control.
template <class DERIVED_TYPE>
class BaseWindow
{
public:
LPCTSTR className = "myWindows";
LPCTSTR Caption = "myWindows";
DWORD Style = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
UINT classStyle = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
DWORD ExStyle = 0;
...
other_property
...
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DERIVED_TYPE *pThis = NULL;
if (uMsg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
if (pThis)pThis->m_hwnd = hwnd;
}else{
pThis = (DERIVED_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(pThis)pThis->m_hwnd = hwnd;
}
if (pThis)
{
return pThis->HandleMessage(hwnd, uMsg, wParam, lParam);
}else{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
ATOM RegClass()
{
WNDCLASS wc = { 0 };
wc.lpfnWndProc = BaseWindow::WindowProc;
wc.hInstance = hInst;
wc.lpszClassName = className;
wc.style = classStyle;
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
return RegisterClass(&wc);
}
BOOL CreateWinEx( LPCTSTR className_,LPCTSTR Caption_,
DWORD Style_,DWORD ExStyle_,
int x_,int y_,int w_,int h_,
HWND hWndParent_,HMENU hMenu_,HINSTANCE hInst_)
{
m_hwnd = CreateWindowEx(
ExStyle_, className_, Caption_, Style_,
x_, y_, w_, h_,
hWndParent_, hMenu_, hInst_, this
);
return (m_hwnd ? TRUE : FALSE);
}
BOOL Create()
{
ATOM rst = RegClass();
if(rst == 0) return FALSE;
m_hwnd = CreateWindowEx(
ExStyle, className, Caption, Style,
x, y, w, h,
hWndParent, hMenu, hInst, this
);
return (m_hwnd ? TRUE : FALSE);
}
}
Based on the above, I have derived two classes, one as the main form and the other as the listview class that I am going to design.
class FirstWindow : public BaseWindow<FirstWindow>
{
public:
FirstWindow(){};
...
some_property
...
LRESULT HandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK ButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
public:
};
class listView : public BaseWindow<listView>
{
public:
listView();
int init(LPCTSTR className_,HWND hWndParent_,int x,int y,int w,int h)
{
this->className = className_;
this->hWndParent = hWndParent_;
this->Style = WS_CHILD| WS_VISIBLE | ~WS_CAPTION;
this->x = x;
this->y = y;
this->w = w;
this->h = h;
this->Create();
hWndMsgWindow = m_hwnd;
CreateListView(hWndMsgWindow);
ShowWindow(hWndMsgWindow,SW_SHOW);
ShowWindow(hWndListView,SW_SHOW);
UpdateWindow(hWndMsgWindow);
UpdateWindow(hWndListView);
idebug("listview::init >> hWndMsgWindow:%d,parent:%d, error:%d\n",hWndMsgWindow,GetParent(hWndMsgWindow), GetLastError());
idebug("listview::init >> hWndListView:%d,parent:%d,error:%d\n",hWndListView,GetParent(hWndListView), GetLastError());
return 1;
}
LRESULT HandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT listView::CreateListView(HWND hwndParent_)
{
InitCommonControls();
bool rst = CreateWinEx(WC_LISTVIEW, NULL,
WS_CHILD | WS_VISIBLE | LVS_REPORT |LVS_EDITLABELS | LVS_NOCOLUMNHEADER | LVS_OWNERDATA |LVS_OWNERDRAWFIXED | WS_BORDER,0,
x,y,w,h,
hwndParent_, NULL, GetModuleHandle(NULL));
if(rst)
hWndListView = m_hwnd;
idebug("CreateListView >> rst:%d,hwndlistview:%d,parent:%d,hwndParent_:%d, error:%d\n",rst,hWndListView,GetParent(hWndListView),hwndParent_, GetLastError());
return rst;
}
LRESULT AddItem();
LRESULT OnListViewNotify(HWND hwnd, LPARAM lParam);
void OwnerDraw(LPDRAWITEMSTRUCT lpdis, HDC hdc, HWND hWnd, const TCHAR* szDraw);
void on_drawItem(HWND hWnd, LPARAM lParwm);
void on_notify(HWND hWnd, WPARAM wParwm, LPARAM lParam);
void on_create(HWND hWnd);
void on_measureItem(LPARAM lParam);
bool add_item();
bool add_item_sub();
...
some_property
...
};
fire FirstWindow first
FirstWindow * pFirstWnd;
listView lv;
int main(int argc, char** argv)
{
FirstWindow win;
pFirstWnd = &win;
win.className = "FirstWindow";
win.Style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
win.x = 100;
win.y = 100;
win.w = 600;
win.h = 300;
bool rst = win.Create();
if (!rst)
{
idebug("getlasterror:%d,line:201\n", GetLastError());
bug.ShowErr();
}
else {
lv.init("mylistview",pFirstWnd->m_hwnd,20,20,300,200);
ShowWindow(lv.m_hwnd, SW_SHOW);
}
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
The goal of the listview_class is to be able to receive and process window messages independently. not like this:
LRESULT FirstWindow::HandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
{
//lv.init("mylistview2",pFirstWnd->m_hwnd,20,20,300,200);
//ShowWindow(lv.m_hwnd, SW_SHOW);
break;
}
case WM_NOTIFY:
{
lv.on_notify(hWnd,wParam,lParam);
break;
}
...
}
default:
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
return TRUE;
}
Instead, I want to process messages independently within listview class,like this:
LRESULT listView::HandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
static HDC hdc;
HRESULT lResult;
switch (uMsg)
{
case WM_CREATE:
{
idebug("class_listview >> WM_CREATE >> hWnd :%d, className:%s, parent:%d\n",hWnd, className , GetParent(hWnd));
//HWND h = CreateWindowEx(0, WC_LISTVIEW, NULL,
WS_CHILD | WS_VISIBLE | LVS_REPORT |LVS_EDITLABELS | LVS_NOCOLUMNHEADER | LVS_OWNERDATA |LVS_OWNERDRAWFIXED | WS_BORDER,
20,20,300,200,
hWnd,NULL, GetModuleHandle(NULL),NULL);
//ShowWindow(h,SW_SHOW);
//UpdateWindow(hWnd);
//idebug("class_listview >> WM_CREATE >> hwnd:%d,parent:%d,error:%d\n",h,GetParent(h), GetLastError());
//ShowWindow(h,SW_SHOW);
//UpdateWindow(h);
break;
}
case WM_NOTIFY:
{
on_notify(hWnd,wParam,lParam);
break;
}
case WM_MEASUREITEM:
{
on_measureItem(lParam);
break;
}
case WM_DRAWITEM:
{
on_drawItem(hWnd,lParam);
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 2));
EndPaint(hWnd, &ps);
idebug("class_listview >> WM_PAINT->hWnd:%d,id:%d,err:%d\n", hWnd, id, GetLastError());
break;
}
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
The problem now is that i can't see other control windows except that FirstWindow can display normally.
update:
some message from debug:
class_listview >> WM_CREATE >> hWnd :1443146, className:mylistview, parent:262350,error:0
listView >> WM_SIZE >> hWnd:1443146,parent:262350
CreateListView >> rst:1,hwndlistview:853318,parent:1443146,hwndParent_:1443146, error:5
listview::init >> hWndMsgWindow:1443146,parent:262350, error:5
listview::init >> hWndListView:853318,parent:1443146,error:5
the key of the problem is the return value of WM_NOTIFY message.
i typed :
case WM_NOTIFY:
{
retnrn on_notify(hWnd,wParam,lParam);
}
the problem was resolved.
how i can move my window when middle mouse button pressed?
in case with left mouse button it would be
case WM_LBUTTONDOWN:
{
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
return 0;
but it does not work with middle button
case WM_MBUTTONDOWN:
{
SendMessage(hwnd, WM_NCMBUTTONDOWN, HTCAPTION, 0);
}
return 0;
but i am not sure about WM_NCMBUTTONDOWN
i`m out of variants, need help please
You have to do this the hard way I'm afraid. I tried all the obvious things and WM_NCHITTEST doesn't cut it if you want to drag the window with anything other than the left mouse button.
Here's some code that works for me (tested on Windows 10). I've excluded error checking for brevity:
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
// WndProc
LRESULT CALLBACK MyWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static int xClick, yClick;
switch (uMsg)
{
case WM_NCMBUTTONDOWN:
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
{
SetCapture (hWnd);
POINT pt = { GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam) };
if (uMsg == WM_NCMBUTTONDOWN)
ScreenToClient (hWnd, &pt);
xClick = pt.x;
yClick = pt.y;
return 0;
}
case WM_LBUTTONUP:
case WM_MBUTTONUP:
ReleaseCapture ();
return 0;
case WM_MOUSEMOVE:
{
if (GetCapture() == hWnd)
{
RECT wr;
GetWindowRect (hWnd, &wr);
int xMouse = GET_X_LPARAM (lParam);
int yMouse = GET_Y_LPARAM (lParam);
int xWindow = wr.left + xMouse - xClick;
int yWindow = wr.top + yMouse - yClick;
SetWindowPos (hWnd, NULL ,xWindow, yWindow, 0, 0 , SWP_NOSIZE | SWP_NOZORDER);
}
return 0;
}
}
return DefWindowProc (hWnd, uMsg, wParam, lParam);
}
// WinMain
int CALLBACK _tWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
HINSTANCE hInstance = NULL;
WNDCLASS wc = { };
wc.lpszClassName = __T ("MyWindowClass");
wc.lpfnWndProc = MyWndProc;
RegisterClass (&wc);
HWND hWnd = CreateWindow (wc.lpszClassName, __T ("My Window"),
WS_CAPTION | WS_BORDER | WS_VISIBLE, 100, 100, 200, 200, NULL, NULL, hInstance, 0);
MSG msg;
while (GetMessage (&msg, NULL, 0, 0))
DispatchMessage (&msg);
return 0;
}
Hope this helps.
I can't understand why I got a loop on BeginPaint function. I have already read posts about this kind of loop but almost all of them recommend: "Don't forget to use BeginPaint function on WM_PAINT message, because it entails subsequent WM_PAINT messages otherwise". This isn't my case. May you give me some advices?
This is my windowclass ("CWindow"):
class CWindow {
public:
CWindow();
virtual ~CWindow();
bool RegisterClass(HINSTANCE hInstance);
bool CreateWnd(HINSTANCE hInstance);
bool Show(int nShow);
private:
HWND handleWindow;
ATOM atom;
bool isRegistered;
bool isCreated;
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void OnPaint();
void OnDestroy();
};
WndProc function.
LRESULT CALLBACK CWindow::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
CWindow* windowPtr = reinterpret_cast<CWindow*> ( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );
PAINTSTRUCT ps;
HDC hdc;
switch( msg ) {
case WM_PAINT:
// There is a loop right here!
hdc = BeginPaint( windowPtr->handleWindow, &ps );
// The code below doesn't executed!
RECT rect;
(void)GetClientRect(windowPtr->handleWindow, &rect);
(void)DrawText(hdc, TEXT("Hello, Windows 98!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint( windowPtr->handleWindow, &ps );
break;
case WM_DESTROY:
windowPtr->OnDestroy();
break;
default:
return DefWindowProc( hWnd, msg, wParam, lParam );
}
return 0;
}
RegisterClass
bool CWindow::RegisterClass(HINSTANCE hInstance)
{
const TCHAR app_name[] = TEXT("HelloWin");
WNDCLASSEX windowClass;
ZeroMemory( &windowClass, sizeof(windowClass) );
windowClass.cbSize = sizeof(windowClass);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = WindowProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = hInstance;
windowClass.hIcon = 0;
windowClass.hIcon = 0;
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
windowClass.hbrBackground = 0;
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = app_name;
windowClass.hIconSm = NULL;
atom = RegisterClassEx( &windowClass );
DWORD errorCode = GetLastError();
if( errorCode ) {
isRegistered = 0;
std::wcout << L"ErrorCode: " << errorCode << std::endl;
} else {
isRegistered = 1;
}
return isRegistered;
}
CreateWindow
bool CWindow::CreateWnd(HINSTANCE hInstance)
{
handleWindow = CreateWindow((PCTSTR)atom, // window class name or atom
TEXT("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
DWORD errorCode = GetLastError();
if( !handleWindow ) {
isCreated = 0;
} else {
isCreated = 1;
}
return isCreated;
}
Show
bool CWindow::Show(int nShow)
{
if( isCreated ) {
ShowWindow( handleWindow, nShow );
return UpdateWindow( handleWindow );
} else {
return 0;
}
}
WinMain
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevINstance, LPSTR lpCmdLine, int nShow )
{
CWindow window;
window.RegisterClass( hInstance );
window.CreateWnd( hInstance );
window.Show( nShow );
int response = 0;
MSG msg;
while( GetMessage( &msg, 0, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return 0;
}
Since you never call SetWindowLongPtr,
CWindow* windowPtr = reinterpret_cast<CWindow*>( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );
returns a nullptr, that you subsequently try to dereference:
BeginPaint( windowPtr->handleWindow, &ps )
That will trigger an access violation exception, causing the BeginPaint call to never even get executed, leaving the invalid region as is. As a consequence, the system keeps generating WM_PAINT messages. That's the same issue as not calling BeginPaint altogether.1
To solve this, you'll either have to attach the window handle to the window instance by calling SetWindowLongPtr, or simply use the hWnd parameter that's passed into your CWindow::WindowProc.
1 Note that the system silently handles unhandled exceptions in your WindowProc on 64-bit versions of Windows under certain conditions.
Here is my code. I'm trying to create an edit control. It's not showing, however. Can some take a look at my code and point out the errors please. I can't figure out where the error is. I feel It might have something to do with the parent child relationship.
#include <cstdlib>
#include <windows.h>
#define MAINWINDOW_CLASS_ID 140;
const char* MAINWINDOW_CLASS_NAME = "Main Window";
const char* TEXTAREA_CLASS_NAME = "EDIT";
using namespace std;
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam);
/*
* Initialize the window class and register it.
*/
BOOL initInstance(HINSTANCE hInstance);
/*
* Create and show the window
*/
HWND initWindow(HINSTANCE hInstance);
HWND createTextArea(HWND hParent);
/*
*
*/
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hprevInstance, LPSTR lpCmdline, INT cmdlShow)
{
if (!initInstance(hInstance))
return 0;
HWND hwndMain = initWindow(hInstance);
ShowWindow(hwndMain, cmdlShow);
MSG msg = {} ;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
BOOL initInstance(HINSTANCE hInstance)
{
WNDCLASS wc = {};
wc.lpfnWndProc = WinProc;
wc.hInstance = hInstance,
wc.lpszClassName = MAINWINDOW_CLASS_NAME;
return RegisterClass(&wc);
}
HWND initWindow(HINSTANCE hInstance)
{
HWND hwndMain = CreateWindow(
MAINWINDOW_CLASS_NAME, // The class name
"Text Editor", // The window name
WS_OVERLAPPEDWINDOW,// The window style
CW_USEDEFAULT, // The x pos
CW_USEDEFAULT, // The y pos
CW_USEDEFAULT, // The width
CW_USEDEFAULT, // The height
(HWND) NULL, // Handle to parent
(HMENU) NULL, // Handle to the menu
hInstance, // Handle to the instance
NULL // Window creation data
);
if (!hwndMain) {
return NULL;
}
else {
return hwndMain;
}
}
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam) {
HWND htextArea;
char sztestText[] = "I should be able to see this text.";
switch (uMsg) {
case WM_CREATE:
htextArea = createTextArea(hwnd);
SendMessage(htextArea, WM_SETTEXT, 0, (LPARAM) sztestText);
break;
case WM_PAINT:
break;
case WM_CLOSE:
break;
case WM_SIZE:
RECT rectMainWindow;
GetWindowRect(hwnd, &rectMainWindow);
INT x = rectMainWindow.right - rectMainWindow.left + 50;
INT y = rectMainWindow.bottom - rectMainWindow.top + 50;
MoveWindow(htextArea, 0, 0, x, y, TRUE);
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
/*******************************************************************/
HWND createTextArea(HWND hParent) {
return CreateWindow(
TEXTAREA_CLASS_NAME, // Class control name
NULL, // Title
WS_CHILD | WS_VISIBLE | ES_MULTILINE, // Styles
0, 0, 0, 0, // Sizing and position
hParent,
(HMENU) MAKEINTRESOURCE(100),
(HINSTANCE) GetWindowLong(hParent, GWL_HINSTANCE),
NULL
);
}
You've defined htextArea as a local variable in WinProc and so every time your window procedure is called its value will be uninitialized. You should make it static or move it to global data outside the function.
(The actual problem is that when you get a WM_SIZE message, you've lost the handle to the edit control, and so its size remains as 0,0).
MSDN doc's for MoveWindow() says:
"If the bRepaint parameter is TRUE, the system sends the WM_PAINT message to the window procedure immediately after moving the window (that is, the MoveWindow function calls the UpdateWindow function)."
But when I call GetUpdateRect() after MoveWindow(), while processing the WM_LBUTTONDOWN message in the parent, I get a beep, which shows that the child is invalid. What is the explanation ???
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
HINSTANCE ghInstance;
LRESULT CALLBACK WindowProc (HWND hwnd, UINT message, UINT wParam, LONG lParam);
LRESULT CALLBACK ChildProc (HWND hwnd, UINT message, UINT wParam, LONG lParam);
int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASSEX wndclassx;
ghInstance = hInstance;
wndclassx.cbSize = sizeof(WNDCLASSEX);
wndclassx.style = 0;
wndclassx.lpfnWndProc = WindowProc;
wndclassx.cbClsExtra = 0;
wndclassx.cbWndExtra = 0;
wndclassx.hInstance = hInstance;
wndclassx.hIcon = 0;
wndclassx.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclassx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclassx.lpszMenuName = NULL;
wndclassx.lpszClassName = _T("ParentWindow");
wndclassx.hIconSm = NULL;
if( !RegisterClassEx(&wndclassx) ) return 0;
wndclassx.cbSize = sizeof(WNDCLASSEX);
wndclassx.style = 0;
wndclassx.lpfnWndProc = ChildProc;
wndclassx.cbClsExtra = 0;
wndclassx.cbWndExtra = 0;
wndclassx.hInstance = hInstance;
wndclassx.hIcon = 0;
wndclassx.hCursor = 0;
wndclassx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclassx.lpszMenuName = NULL;
wndclassx.lpszClassName = _T("ChildWindow");
wndclassx.hIconSm = NULL;
if( !RegisterClassEx(&wndclassx) ) return 0;
if( !(hWnd = CreateWindow(_T("ParentWindow"), _T("Parent Window"), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance,
NULL)) ) return 0;
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
while( GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WindowProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
HWND hWnd;
switch ( message )
{
case WM_CREATE:
CreateWindow(_T("ChildWindow"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 10, 100, 100, hwnd, (HMENU)0,
ghInstance, NULL);
break;
case WM_LBUTTONDOWN:
hWnd = GetWindow(hwnd, GW_CHILD);
MoveWindow(hWnd, 10, 10, 200, 200, true);
if( GetUpdateRect(hWnd, NULL, FALSE) ) MessageBeep(-1);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK ChildProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
return DefWindowProc(hwnd, message, wParam, lParam);
}
The MSDN doc's is wrong. MoveWindow() with TRUE doesn't call UpdateWindow() as the documentation says. It just invalidates the window client area. If I call UpdateWindow() just after MoveWindow() the program runs as expected.
I tried it myself and WM_PAINT is triggered before the if(GetUpdateRect()) is. Also, GetUpdateRect returns FALSE for me.
I'm running Visual Studio 2008 on XP.
I guess it could depend on what compiler you are using, what operating system that is used and whatnot. According to the code you passed everything is done in the same thread, but if it is a multithreaded program I think that this could inflict some issues as well.