I'm trying to make a screenshot helper that takes screenshots when it receives signal from other application. In my design, the higher layer application will send a window message to the helper, and the helper will then take a screenshot for user (by simulating Win+Prtsc). I've done this without any problem, but when I try to add a feature that implements by detecting the gap between two message, I found that GetMessageTime() function works weird.
The problem is that I added a MessageBox for debugging, and GetMessageTime returns the proper ticks the message was sent at. But when I removed that MessageBox, GetMessageTime kept returning 0. I searched the web and MSDN documents but found nothing about this. So I'm just wondering what happened and what should I do, without changing the architecture that using HWND_MESSAGE window to receive message.
MessageReceiver
#include <Windows.h>
#include <stdio.h>
HWND hwndMain;
UINT hotkeyTriggeredMessageId;
INPUT winPlusPrtscKeyCombo[4];
LONG lastHotkeyTime = -1000;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == hotkeyTriggeredMessageId) {
printf("last:%d,this:%d,diff:%d\n", lastHotkeyTime, GetMessageTime(), lastHotkeyTime - GetMessageTime());
MessageBox(NULL, L"triggered!", L"", NULL);
printf("last:%d,this:%d,diff:%d\n", lastHotkeyTime, GetMessageTime(), lastHotkeyTime - GetMessageTime());
SendInput(4, winPlusPrtscKeyCombo, sizeof(INPUT));
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
int main() {
MSG msg;
BOOL bRet;
WNDCLASS wc = { 0 };
memset(winPlusPrtscKeyCombo, 0, sizeof(INPUT) * 4);
winPlusPrtscKeyCombo[0].type = INPUT_KEYBOARD;
winPlusPrtscKeyCombo[0].ki.wVk = VK_LWIN;
winPlusPrtscKeyCombo[1].type = INPUT_KEYBOARD;
winPlusPrtscKeyCombo[1].ki.wVk = VK_SNAPSHOT;
winPlusPrtscKeyCombo[2].type = INPUT_KEYBOARD;
winPlusPrtscKeyCombo[2].ki.wVk = VK_SNAPSHOT;
winPlusPrtscKeyCombo[2].ki.dwFlags = KEYEVENTF_KEYUP;
winPlusPrtscKeyCombo[3].type = INPUT_KEYBOARD;
winPlusPrtscKeyCombo[3].ki.wVk = VK_LWIN;
winPlusPrtscKeyCombo[3].ki.dwFlags = KEYEVENTF_KEYUP;
HINSTANCE hInstance = GetModuleHandle(NULL);
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"MessageReceiverWindow";
if (!RegisterClass(&wc)) {
return FALSE;
}
hwndMain = CreateWindow(L"MessageReceiverWindow", L"MessageReceiverWindow", 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInstance, 0);
hotkeyTriggeredMessageId = RegisterWindowMessage(L"MESSAGE_HOTKEY_TRIGGERED");
ChangeWindowMessageFilterEx(hwndMain, hotkeyTriggeredMessageId, MSGFLT_ALLOW, NULL);
UpdateWindow(hwndMain);
while (GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return 0;
}
MessageSender
#include <stdio.h>
#include <Windows.h>
UINT hotkeyTriggeredMessageId;
HWND targetWindow;
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) {
hotkeyTriggeredMessageId = RegisterWindowMessage(L"MESSAGE_HOTKEY_TRIGGERED");
targetWindow = FindWindowEx(HWND_MESSAGE, NULL, L"MessageReceiverWindow", NULL);
SendMessage(targetWindow, hotkeyTriggeredMessageId, NULL, NULL);
return 0;
}
Related
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.
The DATETIMEPICKERINFOstructure obtained by sending the DTM_GETDATETIMEPICKERINFOmessage has a field hwndEdit which might be what I'm looking for. However, I'm getting always NULL for it so I'm wondering what's its actual meaning. If not, is there a way to get the handle of the entry field?
hwndEdit only seems to be valid when the control has the DTS_APPCANPARSE style and you click the date text with the mouse (I tested this with OutputDebugString and a timer). The edit control is created and destroyed dynamically. The hwndUD handle is only valid if DTS_UPDOWN is set and the hwndDropDown is only valid while the dropdown is visible.
It is not called out in the documentation but DTM_GETDATETIMEPICKERINFO is marked Vista+ and this often means the feature is only implemented in ComCtl32 v6 so you also have to make sure you have a manifest that requests this version.
To change the color you can try DTM_SETMCCOLOR but only MCSC_BACKGROUND is documented to work when Visual Styles are active.
I'm afraid there is no way to get what you wanted. I just created a simple Win32 application just to test the possibility. If I use the DTM_GETDATETIMEPICKERINFO, hwndDropDown, hwndEdit and hwndUD give me NULL. If I try to enum child window, well before I do so I check it with Spy++, no luck, there is no child window associated with it.
Finally, I tried GetFocus() and WindowFromPoint(), both give me the HWND of the DateTimePicker itself only.
Here is my testing code:
#pragma comment(lib, "comctl32.lib")
#include <windows.h>
#include <tchar.h>
#include <commctrl.h>
enum MYID {
MYID_FIRST = WM_APP,
MYID_DTP
};
LPCTSTR const g_MyWndClass = _T("DTPTest");
LPCTSTR const g_MyWndTitle = _T("DTPTest");
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void OnWindowCreate(HWND);
void OnTimer(HWND);
int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int nCmdShow)
{
INITCOMMONCONTROLSEX icex{};
icex.dwSize = sizeof(icex);
icex.dwICC = ICC_DATE_CLASSES;
InitCommonControlsEx(&icex);
WNDCLASSEX wcex{};
wcex.cbSize = sizeof(wcex);
wcex.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.lpfnWndProc = WndProc;
wcex.lpszClassName = g_MyWndClass;
wcex.style = CS_HREDRAW | CS_VREDRAW;
RegisterClassEx(&wcex);
HWND hwnd = CreateWindowEx(0,
g_MyWndClass, g_MyWndTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, 600, 400,
nullptr, nullptr, nullptr, nullptr);
if (!hwnd) { return 99; }
SetTimer(hwnd, 0, 100, nullptr);
ShowWindow(hwnd, nCmdShow);
MSG msg{};
while (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return static_cast<int>(msg.wParam);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM w, LPARAM l)
{
switch (msg) {
case WM_CREATE:
OnWindowCreate(hwnd);
break;
case WM_TIMER:
OnTimer(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, w, l);
}
return 0;
}
void OnWindowCreate(HWND hwnd)
{
HWND hwndDTP = CreateWindowEx(0, DATETIMEPICK_CLASS, nullptr,
WS_CHILD | WS_VISIBLE | DTS_SHOWNONE,
20, 50, 220, 20,
hwnd, reinterpret_cast<HMENU>(MYID_DTP), nullptr, nullptr);
DATETIMEPICKERINFO info{};
info.cbSize = sizeof(DATETIMEPICKERINFO);
SendMessage(hwndDTP, DTM_GETDATETIMEPICKERINFO, 0,
reinterpret_cast<LPARAM>(&info));
if (!info.hwndDropDown && !info.hwndEdit && !info.hwndUD)
{
MessageBox(hwnd, _T("No luck with DTM_GETDATETIMEPICKERINFO"),
nullptr, MB_ICONERROR);
}
}
void OnTimer(HWND hwnd)
{
POINT pt{};
GetCursorPos(&pt);
HWND hwndPoint = WindowFromPoint(pt);
HWND hwndFocus = GetFocus();
TCHAR buf[99]{};
wsprintf(buf, _T("Pointing at %p, focusing %p"),
hwndPoint, hwndFocus);
SetWindowText(hwnd, buf);
}
I have a program that is supposed to display a count that increments every second. The counter is in a separate thread. I call WindowUpdate when a change has occurred but the count in the window does not update unless I hover the mouse pointer over the window or resize the window. I have tried InvalidateRect and RedrawWindow but they don't work either.
Why won't the counter updates display?
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <sdkddkver.h>
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <strsafe.h>
typedef struct DataTransfer {
BOOL Updated;
int Counter;
} TRANSFER, *PTRANSFER;
TRANSFER Payload;
PTRANSFER pPayload;
DWORD dwThreadId;
HANDLE hThread;
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void DisplayData(HDC hDC);
void ErrorHandler(LPTSTR lpszFunction);
DWORD WINAPI Counter(LPVOID lpParam);
int
APIENTRY
wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd)
{
MSG msg;
WNDCLASSEX wcex;
ZeroMemory(&wcex, sizeof(wcex));
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpszClassName = TEXT("MYFIRSTWINDOWCLASS");
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.hCursor = LoadCursor(hInstance, IDC_ARROW);
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
if (!RegisterClassEx(&wcex))
return 1;
CREATESTRUCT cs;
ZeroMemory(&cs, sizeof(cs));
cs.x = 0;
cs.y = 0;
cs.cx = 200;
cs.cy = 300;
cs.hInstance = hInstance;
cs.lpszClass = wcex.lpszClassName;
cs.lpszName = TEXT("Test");
cs.style = WS_OVERLAPPEDWINDOW;
HWND hWnd = ::CreateWindowEx(
cs.dwExStyle,
cs.lpszClass,
cs.lpszName,
cs.style,
cs.x,
cs.y,
cs.cx,
cs.cy,
cs.hwndParent,
cs.hMenu,
cs.hInstance,
cs.lpCreateParams);
if (!hWnd)
return 1;
DWORD dwThreadId;
HANDLE hThread;
pPayload = (PTRANSFER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TRANSFER));
if (pPayload == NULL)
ExitProcess(2);
pPayload->Updated = FALSE;
pPayload->Counter = 0;
// Display the window.
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
hThread = CreateThread(
NULL,
0,
Counter,
pPayload,
0,
&dwThreadId);
if (hThread == NULL)
ExitProcess(2);
while (1)
{
if (pPayload->Updated == TRUE)
{
// InvalidateRect(hWnd, NULL, FALSE);
// RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
UpdateWindow(hWnd);
}
if (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
::UnregisterClass(wcex.lpszClassName, hInstance);
return (int)msg.wParam;
}
LRESULT
CALLBACK
WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT paintStruct;
HDC hDC;
switch (uMsg)
{
case WM_DESTROY:
::PostQuitMessage(0);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &paintStruct);
DisplayData(hDC);
EndPaint(hWnd, &paintStruct);
return 0;
break;
default:
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
void DisplayData(HDC hDC)
{
char OutputStr[32];
sprintf_s(OutputStr, sizeof(OutputStr) - 1, "%d", pPayload->Counter);
TextOut(hDC, 100, 100, OutputStr, strlen(OutputStr));
}
DWORD WINAPI Counter(LPVOID lpParam)
{
PTRANSFER pTransfer;
pTransfer = (PTRANSFER)lpParam;
while (1)
{
pTransfer->Counter++;
pTransfer->Updated = TRUE;
Sleep(1000);
}
}
void ErrorHandler(LPTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
(Disclaimer: This answer was written by the seat of my pants. Please correct me if I made an error somewhere.)
You are not doing what you are trying to do properly.
First, as Jonathan Potter has noted, UpdateWindow() by itself will not update the window unless it has an invalid region. The InvalidateRect() call will invalidate a rectangle. So you need both...
...but in reality you don't really want to call UpdateWindow() directly, because it bypasses the Windows drawing model. After calling InvalidateRect(), if there are no pending messages next time GetMessage() is called, and Windows itself decides it's time to refresh the screen contents with new data, you'll get a WM_PAINT message. The system knows when it's best to paint; you'll make your life easier by using it. Only use UpdateWindow() when it is vital that you want the window to redraw right now.
(The point of the invalid region is also due to the Windows drawing model: drawing can be very expensive, so Windows tries to optimize drawing by only redrawing what is needed. Invalidate only the part of your window that needs to be updated, and you'll get better performance.)
But there is a deeper issue: you do not implement multithreading properly.
Your worker thread is generating data every second and overwriting a shared structure. Your window is reading from that shared structure. There is nothing in place to ensure that only one thread is accessing that shared structure at a time. As a result, you'll wind up with ptential mixed state if your worker thread ever grows from a simple integer to a large and complex data structure.
You need to synchronize your data accesses. Communicate between threads.
How? The obvious method is to use a synchronization object, like a mutex. And you can totally do that.
But there's a better way: since one of your threads has a window, just use a window message! Window messages sent using SendMessage() will be received on the window's thread (with the calling thread blocked); messages posted using PostMessage() will be placed on the window's thread's message queue.
Plus you can pass not one but two pieces of information in that message! Both wParam and lParam are pointer-sized integers, so just stuff a pointer in one of them and use SendMessage() or PostMessage()!
But what message do you use? Every* message in the range [WM_USER, WM_APP) is available to the window class to decide what to use it for, and every message in the range [WM_APP, 0xC000) is for the application to decide what to use it for. So just pick one and use it!
Just remember how SendMessage() and PostMessage() work. If the data is allocated on the heap, and each piece of data is allocated separately, then it doesn't matter; if the data is a pointer to a local variable, SendMessage() is the correct answer; if the data is a numeric value that can fit in a pointer-sized integer, either will work. If you need a response, your worker thread will either need a window of its own or the use of SendMessage() to get a value back from the window.
There are lots of ways to do this. It all depends on what data you need to communicate. As the Go programming language's designers say (altered somewhat): don't communicate by sharing data; share data by communicating.
*until you call IsDialogMessage(), at which point you lose WM_USER and WM_USER + 1 (and possibly WM_USER + 2 as well?). But that's only three messages out of over 32,000.
Your problem is in your message loop:
while (1)
{
if (pPayload->Updated == TRUE) {
UpdateWindow(hWnd);
}
if (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
GetMessage is synchronous, and doesn't return until a message is available for retrieval. The code sits there, waiting for a message to arrive most of the time, and will not evaluate pPayload->Updated. Consequently, no updates happen, until a message arrives. But even then, nothing happens, since UpdateWindow sends "a WM_PAINT message to the window if the window's update region is not empty." UpdateWindow by itself doesn't do anything useful.1)
You could solve this by reworking your message loop. The following issues need to be addressed:
A call to InvalidateRect must precede the call to UpdateWindow.
Access to the shared data must be synchronized.
The message loop needs to terminate, when GetMessage returns 0 or -1.
None of this is necessary, though. You don't need a background thread. A single-threaded application will do just fine. Just use a Timer to trigger updates to the stored value, and the visual representation. Here is a rough sketch for a window procedure implementing a timer-based solution:
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static unsigned __int64 startTime = ::GetTickCount64();
const UINT_PTR timerId = 1;
switch (uMsg)
{
case WM_CREATE:
// Start timer when window is created
::SetTimer(hWnd, timerId, 1000, nullptr);
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
case WM_TIMER: {
unsigned __int64 currTime = ::GetTickCount64();
Counter = (currTime - startTime) / 1000;
// Value changed -> initiate window update
::InvalidateRect(hWnd, nullptr, FALSE);
// Re-start timer
::SetTimer(hWnd, timerId, 1000, nullptr);
}
break;
case WM_DESTROY:
::KillTimer(hWnd, timerId);
::PostQuitMessage(0);
break;
case WM_PAINT: {
PAINTSTRUCT paintStruct = {0};
HDC hDC = BeginPaint(hWnd, &paintStruct);
DisplayData(hDC);
EndPaint(hWnd, &paintStruct);
}
return 0;
default:
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
A few notes on the implementation:
The timer uses a fixed time-out value of 1000 milliseconds. This will occasionally skip an update. The error doesn't accumulate, though, as the Counter is re-evaluated whenever the timer expires. If you more steady updates, calculate the remaining time-out value based on the Counter, startTime, and currTime.
Counter is the variable that holds the current time in seconds. It is a replacement for TRANSFER::Counter, that needs to be accessible from both the window procedure and the rendering code.
1) There is another issue with your message loop: It never terminates, and neither does your process. The GUI may disappear, but the process will still show up in Task Manager.
i marked my alterations with comment <<updated
instead of setting flag Updated, i changed it to be hWnd, and then update the window instantly and directly from the thread
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <sdkddkver.h>
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <strsafe.h>
typedef struct DataTransfer {
//BOOL Updated; // << updated
int Counter;
HWND hWnd; // << updated
} TRANSFER, *PTRANSFER;
TRANSFER Payload;
PTRANSFER pPayload;
DWORD dwThreadId;
HANDLE hThread;
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void DisplayData(HDC hDC);
void ErrorHandler(LPTSTR lpszFunction);
DWORD WINAPI Counter(LPVOID lpParam);
int
APIENTRY
wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd)
{
MSG msg;
WNDCLASSEX wcex;
ZeroMemory(&wcex, sizeof(wcex));
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpszClassName = TEXT("MYFIRSTWINDOWCLASS");
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); //<< updated
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
if (!RegisterClassEx(&wcex))
return 1;
CREATESTRUCT cs;
ZeroMemory(&cs, sizeof(cs));
cs.x = 0;
cs.y = 0;
cs.cx = 200;
cs.cy = 300;
cs.hInstance = hInstance;
cs.lpszClass = wcex.lpszClassName;
cs.lpszName = TEXT("Test");
cs.style = WS_OVERLAPPEDWINDOW;
HWND hWnd = CreateWindowEx(
cs.dwExStyle,
cs.lpszClass,
cs.lpszName,
cs.style,
cs.x,
cs.y,
cs.cx,
cs.cy,
cs.hwndParent,
cs.hMenu,
cs.hInstance,
cs.lpCreateParams);
if (!hWnd)
return 1;
DWORD dwThreadId;
HANDLE hThread;
pPayload = (PTRANSFER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TRANSFER));
if (pPayload == NULL)
ExitProcess(2);
//pPayload->Updated = FALSE; //<< updated
pPayload->hWnd=hWnd;
pPayload->Counter = 0;
// Display the window.
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
hThread = CreateThread( NULL, 0, Counter, pPayload, 0, &dwThreadId);
if (hThread == NULL)
ExitProcess(2);
while (1)
{
// ____[ updated ]_____
/*
if (pPayload->Updated == TRUE)
{
UpdateWindow(hWnd);
pPayload->Updated=FALSE;
}
*/
int ret=GetMessage(&msg, NULL, 0, 0);//<< updated
if(ret==0 || ret==-1)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnregisterClass(wcex.lpszClassName, hInstance);
return (int)msg.wParam;
}
LRESULT
CALLBACK
WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT paintStruct;
HDC hDC;
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &paintStruct);
DisplayData(hDC);
EndPaint(hWnd, &paintStruct);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
void DisplayData(HDC hDC)
{
char OutputStr[32];
sprintf_s(OutputStr, sizeof(OutputStr) - 1, "%d", pPayload->Counter);
TextOut(hDC, 100, 100, OutputStr, strlen(OutputStr));
}
DWORD WINAPI Counter(LPVOID lpParam)
{
PTRANSFER pTransfer;
pTransfer = (PTRANSFER)lpParam;
while (1)
{
pTransfer->Counter++;
InvalidateRect(pTransfer->hWnd,NULL,1);
Sleep(1000);
}
}
void ErrorHandler(LPTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
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.