I create a window and message loop in the child thread.
When I sent a custom message through PostMessage, when I called DestroyWindow in the thread, DestroyWindow blocked and did not trigger WM_DESTORY, so the message loop thread cannot exit normally.
I tried PostThreadMessage, In addition, I tried to use closewindow. But the problem is that WM_DESTORY is not triggered after calling DestroyWindow, not the PostMessage.
Message loop thread is still running and window handle is valid, why? I worked hard for a few days and couldn't find the reason, thank you very much.
Destroy window:
#define WM_QUIT_MSG_LOOP (WM_USER+8600)
mfxStatus CD3D11Device::DeleteRenderChildWindow()
{
LOG(LS_INFO) << "Intel D3D11Render, Enter DeleteRenderChildWindow, HWND:" << m_hChildHwnd;
//SetParent(m_hChildHwnd, NULL);
if (m_hChildHwnd){
LOG(LS_WARNING) << "Intel D3D11Render, DeleteRenderChildWindow, HWND:" << m_hChildHwnd;
PostMessage(m_hChildHwnd, WM_QUIT_MSG_LOOP, NULL, NULL);
}
if(m_pChildWindowMsgThread)
m_pChildWindowMsgThread->Wait();
MSDK_SAFE_DELETE(m_pChildWindowMsgThread);
MSDK_SAFE_DELETE(m_pCreateFinishEvent);
LOG(LS_INFO) << "Intel D3D11Render, Leave DeleteRenderChildWindow, HWND:" << m_hChildHwnd;
return MFX_ERR_NONE;
}
Create window:
LRESULT CALLBACK CD3D11Device::ChildRenderMsgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
LRESULT lResult = 0;
switch (uMsg) {
case WM_DESTROY: {
LOG(LS_WARNING) << "Intel D3D11Render ChildRenderMsgProc, PostQuitMessage, HWND:" << hwnd;
PostQuitMessage(0);
break;
}
case WM_SETCURSOR: {
break;
}
default:
lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
break;
}
return lResult;
}
HWND CD3D11Device::ThreadCreateChildWindow(){
HMODULE hInstance = nullptr;
BOOL result =
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<char*>(&DefWindowProc), &hInstance);
if (!result) {
LOG(LS_ERROR) << "[ThreadCreateChildWindow]GetModuleHandleExA failed.";
return 0;
}
// Register the host window class. See the MSDN documentation of the
WNDCLASSEXW wcex = {};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = &ChildRenderMsgProc;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.lpszClassName = _T("Render Window Class");
//wcex.style |= CS_HREDRAW | CS_VREDRAW &~WS_CAPTION &~WS_SYSMENU;
// Ignore the error which may happen when the class is already registered.
RegisterClassExW(&wcex);
RECT rcClient = { 0 };
GetWindowRect(m_HandleWindow, &rcClient);
// Create the host window.
HWND hChildWindow =
CreateWindowW(_T("Render Window Class"), _T("MiniRender"), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, 0,
0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, m_HandleWindow, nullptr, hInstance, nullptr);
if (!hChildWindow) {
LOG(LS_ERROR) << "[ThreadCreateChildWindow]Create child window failed.";
return 0;
}
ShowWindow(hChildWindow, SW_SHOW);
SetRenderChildHwnd(hChildWindow);
m_pCreateFinishEvent->Signal();
LOG(LS_WARNING) << "Intel D3D11Render, ThreadCreateChildWindow, HWND:" << hChildWindow;
return hChildWindow;
}
Message loop thread:
unsigned int CD3D11Device::ChildWindowMsgThread(void* ctx){
CD3D11Device* pD3D11Device = static_cast<CD3D11Device*>(ctx);
HWND hChildWindow = NULL;
if (pD3D11Device) {
hChildWindow = pD3D11Device->ThreadCreateChildWindow();
}
if (hChildWindow == NULL){
return 0;
}
MSG msg;
BOOL result;
while ((result = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (result == -1) {
LOG(LS_ERROR) << "Intel D3D11Render, ChildWindowMsgThread, GetMessage failed, HWND:" << hChildWindow;
continue;
}
if (msg.message == WM_QUIT_MSG_LOOP) {
LOG(LS_WARNING) << "Intel D3D11Render, ChildWindowMsgThread, recv WM_QUIT_MSG_LOOP, HWND:" << hChildWindow;
//It's blocking up here
DestroyWindow(hChildWindow);
}
else {
PostMessage(pD3D11Device->GetParentHwnd(), msg.message, msg.wParam, msg.lParam);
}
LOG(LS_WARNING) << "Intel D3D11Render, ChildWindowMsgThread, GetMessageing, HWND:" << hChildWindow;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
A window has a thread affinity. Only the thread that created the window can destroy the window (and receive and dispatch messages for the window). This is clearly stated in the documentation:
A thread cannot use DestroyWindow to destroy a window created by a different thread.
Related
My operating system is Windows 11.
I want to make a console program hide in the system tray. So I create a hiding window and use Shell_NotifyIcon to implement it.
When I run the program, not as administrator, the program works well. However, when I run the program as administrator, the uCallbackMessage message of notifyIcon doesn't send to the message queue. I don't understand why it happens.
BOOL AddNotificationIcon()
{
NOTIFYICONDATA nid = { sizeof(nid) };
nid.hWnd = message_hwnd;
nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP | NIF_GUID;
nid.uCallbackMessage = WMAPP_NOTIFYCALLBACK;
nid.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
nid.guidItem = icon_guid;
_tcscpy(nid.szTip, TEXT("FanViewer"));
Shell_NotifyIcon(NIM_ADD, &nid);
// NOTIFYICON_VERSION_4 is prefered
nid.uVersion = NOTIFYICON_VERSION_4;
return Shell_NotifyIcon(NIM_SETVERSION, &nid);
}
void Run()
{
std::thread t1([this] {
message_hwnd = CreateWindow(TEXT("MessageWindow"),
TEXT("Message"),
WS_OVERLAPPEDWINDOW | WS_CAPTION,
CW_USEDEFAULT,
CW_USEDEFAULT,
200,
200,
NULL,
NULL,
GetModuleHandle(NULL),
NULL
);
ShowWindow(message_hwnd, SW_HIDE);
auto result = AddNotificationIcon();
if (!result || !(message_hwnd)) {
Close();
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
});
t1.join();
}
I create the NotifyIcon using these codes.
And I try to print some string when the program gets the WMAPP_NOTIFYCALLBACK message like these
LRESULT Proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WMAPP_NOTIFYCALLBACK:
std::cout << "fff" << std::endl;
switch (LOWORD(lParam)) {
case NIN_SELECT:
ShowWindow(console_hwnd, SW_SHOW);
break;
case WM_CONTEXTMENU:
{
POINT const pt = { LOWORD(wParam), HIWORD(wParam) };
ShowContextMenu(hwnd, pt);
}
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
break;
}
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
break;
}
return 0;
}
When I run the program, not as the administrator, I can see "fff" from the console. But there is nothing when I run the program as administrator.
I am developing a console program and I have planned to add a tray icon for it. But it seems that only Win32 GUI program can do something caused by messages. (WndProc())
My message checking loop code snippet: (Independent sub thread)
void WCH_message_loop() {
// Message loop.
MSG msg;
GetMessageW(&msg, nullptr, 0, 0);
Myn = new MyNotify(msg.hwnd);
MyC = new CTrayIcon();
MyC -> CreateTray(msg.hwnd, LoadIconW(NULL, IDI_ERROR), WCH_IDM_OPENPIC, L"Web Class Helper");
constexpr int ShowHide = 121;
constexpr int PrintScreen = 122;
RegisterHotKey(NULL, ShowHide, MOD_CONTROL, 'P');
RegisterHotKey(NULL, PrintScreen, MOD_CONTROL, VK_DOWN); // Register "Ctrl + Down" hotkey.
while (GetMessage(&msg, nullptr, 0, 0)) {
if (msg.message == WM_HOTKEY) {
switch (msg.wParam) {
case ShowHide:
WCH_SetWindowStatus(!WCH_cmd_line);
break;
case PrintScreen:
cout << endl;
WCH_command_list.clear();
WCH_command_list = WCH_split("pi");
WCH_pi();
MyC -> ChangeTray(L"Successfully printed screen");
break;
default:
break;
}
}
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
WndProc() function: (How could I bind it to something or make it take effect?)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
#ifdef _DEBUG
WCH_printlog(WCH_LOG_STATUS_DEBUG, "Starting \"WndProc()\"");
#endif
switch (message) {
case 1025:
switch (lParam) {
case WM_LBUTTONDOWN:
WCH_SetWindowStatus(true);
break;
case WM_RBUTTONDOWN:
WCH_SetTrayIconMenu(hWnd);
break;
}
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case WCH_IDM_SHOWHIDE:
WCH_SetWindowStatus(!WCH_cmd_line);
break;
case WCH_IDM_EXIT:
exit(0);
break;
default:
return DefWindowProcW(hWnd, message, wParam, lParam);
break;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hWnd, message, wParam, lParam);
}
return 0;
}
Dependent code snippet:
class CTrayIcon {
public:
CTrayIcon() {};
~CTrayIcon() {};
BOOL CreateTray(HWND, HICON, UINT, LPCTSTR = L"Web Class Helper");
BOOL ChangeTray(LPCTSTR, LPCTSTR = L"Web Class Helper", UINT = 3000);
BOOL DeleteTray();
private:
NOTIFYICONDATA m_Notify;
};
struct MyNotify {
MyNotify(HWND hWND, WCHAR* Path = (WCHAR*)L"WCHs.ico", WCHAR* Title = (WCHAR*)L"Web Class Helper", UINT uID = 1) {
NOTIFYICONDATA nID = {};
nID.hIcon = (HICON)LoadImageW(NULL, Path, IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
wcscpy_s(nID.szTip, Title);
nID.hWnd = hWND;
nID.uID = uID;
nID.uFlags = NIF_GUID | NIF_ICON | NIF_MESSAGE | NIF_TIP;
nID.uCallbackMessage = 1025;
Shell_NotifyIconW(NIM_ADD, &nID);
}
};
ATOM WCH_RegisterClass(HINSTANCE hInstance, WCHAR* szWindowClass) {
#ifdef _DEBUG
WCH_printlog(WCH_LOG_STATUS_DEBUG, "Registering class");
#endif
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_WCH));
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WCH);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIconW(wcex.hInstance, MAKEINTRESOURCEW(IDI_SMALL));
return RegisterClassExW(&wcex);
}
How could I solve this problem? Thanks!
Now I don't know why does WndProc() do not work. (Maybe just in GUI program?)
But I don't want to convert it to GUI program. 😅
My friend told me that console is actually a GUI window which already has a WndProc() function, so I can not add a WndProc() function more. But can I modify current WndProc() function?
I solved this problem by my friend's suggestion but I don't know why. 🤔
Welcome to point out the key points. 😊
Below is the final complete code snippet.
Overall situation variable:
HMENU WCH_hMenu;
WndProc() function:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
// Window processing module.
NOTIFYICONDATA nid {};
switch (message) {
case WM_CREATE:
#ifdef _DEBUG
WCH_printlog(WCH_LOG_STATUS_DEBUG, "Entering \"WndProc()\": \"WM_CREATE\"");
#endif
nid.cbSize = sizeof(nid);
nid.hWnd = hWnd;
nid.uID = 0;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_USER;
nid.hIcon = (HICON)LoadImageW(NULL, L"WCHS.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
wcscpy(nid.szTip, WCH_window_title.c_str());
Shell_NotifyIconW(NIM_ADD, &nid);
WCH_hMenu = CreatePopupMenu();
AppendMenuW(WCH_hMenu, MF_STRING, WCH_IDM_SHOWHIDE, L"Show");
AppendMenuW(WCH_hMenu, MF_SEPARATOR, 0, NULL);
AppendMenuW(WCH_hMenu, MF_STRING, WCH_IDM_EXIT, L"Quit");
break;
case WM_USER:
if (lParam == WM_LBUTTONDOWN) {
#ifdef _DEBUG
WCH_printlog(WCH_LOG_STATUS_DEBUG, "Entering \"WndProc()\": \"WM_USER\" & \"WM_LBUTTONDOWN\"");
#endif
WCH_SetWindowStatus(true);
} else if (lParam == WM_RBUTTONDOWN) {
POINT pt;
int xx;
GetCursorPos(&pt);
SetForegroundWindow(hWnd);
xx = TrackPopupMenu(WCH_hMenu, TPM_RETURNCMD, pt.x, pt.y, NULL, hWnd, NULL);
#ifdef _DEBUG
WCH_printlog(WCH_LOG_STATUS_DEBUG, "Entering \"WndProc()\": \"WM_USER\" & \"WM_RBUTTONDOWN\" & \"xx = " + to_string(xx) + "\"");
#endif
if (xx == IDR_SHOW) {
#ifdef _DEBUG
WCH_printlog(WCH_LOG_STATUS_DEBUG, "Entering \"WndProc()\": \"WM_USER\" & \"WM_RBUTTONDOWN\" & \"IDR_SHOW\"");
#endif
WCH_SetWindowStatus(true);
} else if (xx == IDR_QUIT) {
#ifdef _DEBUG
WCH_printlog(WCH_LOG_STATUS_DEBUG, "Entering \"WndProc()\": \"WM_USER\" & \"WM_RBUTTONDOWN\" & \"IDR_QUIT\"");
#endif
WCH_command_list.clear();
WCH_command_list.push_back("quit");
cout << endl;
exit(0);
} else if (xx == 0) {
PostMessageW(hWnd, WM_LBUTTONDOWN, NULL, NULL);
}
}
break;
case WM_DESTROY:
#ifdef _DEBUG
WCH_printlog(WCH_LOG_STATUS_DEBUG, "Entering \"WndProc()\": \"WM_DESTROY\"");
#endif
Shell_NotifyIconW(NIM_DELETE, &nid);
PostQuitMessage(0);
break;
default:
if (message == RegisterWindowMessageW(L"TaskbarCreated")) {
SendMessageW(hWnd, WM_CREATE, wParam, lParam);
}
break;
}
return DefWindowProcW(hWnd, message, wParam, lParam);
}
Message handling sub thread module:
void WCH_message_loop() {
// Message loop.
HWND hwnd {};
MSG msg {};
WNDCLASS wndclass {};
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = NULL;
wndclass.hIcon = LoadIconW(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursorW(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = WCH_window_title.c_str();
if (!RegisterClassW(&wndclass)) {
MessageBoxW(NULL, L"This program requires Windows NT!", WCH_window_title.c_str(), MB_ICONERROR);
exit(0);
}
hwnd = CreateWindowExW(WS_EX_TOOLWINDOW, WCH_window_title.c_str(), WCH_window_title.c_str(), WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL);
ShowWindow(hwnd, SW_HIDE);
UpdateWindow(hwnd);
while (GetMessageW(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
The correct way to do this is to create a GUI program with a hidden main window.
When your program starts, alloc a console and attach the standard streams.
So basically I have this program that makes a transparent overlay for a game so I can make a kill counter. However, when I click on the game with the overlay on, nothing happens. I managed to make it so when you click on it then it sends a message to the game telling it to shoot, however, when I tried the same for moving my characters head it was just laggy and snappy. When I would move my head quickly, my cursor would also fly out of the game window. How can I fix this so when I play the game it would be like its not even there.
I have tried sending the message and setting the window active AND using setcapture. However, none of these worked. I have tried looking at other places but they didn't work either.
/* This is my while(true) loop: */
while (TRUE)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
break;
}
TranslateMessage(&msg); // translates virtual-key messages into character messages
DispatchMessage(&msg); // dispatches a message to WindowProc
}
if (gameProcId == 0)
{
gameProcId = GetProcId(L"ac_client.exe");
}
if (gameWnd == NULL)
{
gameWnd = FindWindow(NULL, L"AssaultCube");
}
if ((gameProc == NULL) && (gameProcId != 0))
{
gameProc = OpenProcess(PROCESS_VM_READ, false, gameProcId); // opens an existing local process and returns a handle to it
}
if (gameProc != NULL)
{
if ((!init_ok) || ((loops % 20) == 0))
{
RECT client_rect;
#pragma warning (suppress: 6387)
GetClientRect(gameWnd, &client_rect); // gets a windows coordinates, upper-left corner is (0,0)
w_res.X = client_rect.right;
w_res.Y = client_rect.bottom;
RECT bounding_rect;
#pragma warning (suppress: 6387)
GetWindowRect(gameWnd, &bounding_rect); // gets dimensions of a window
if (!init_ok)
{
if ((w_pos.X != bounding_rect.left) || (w_pos.Y != bounding_rect.top))
{
MoveWindow(hWnd, bounding_rect.left, bounding_rect.top,
client_rect.right, client_rect.bottom, false);
w_pos.X = bounding_rect.left;
w_pos.Y = bounding_rect.top;
}
//SetCursorPos(w_pos.X * 4, w_pos.Y * 4);
//ClipCursor(&gameRect);
}
else
{
if ((bounding_rect.left == 0) && (bounding_rect.top == 0))
{
MoveWindow(hWnd, bounding_rect.left, bounding_rect.top, // changes both the position and dimension of a window
client_rect.right, client_rect.bottom, false);
}
MoveWindow(hWnd, bounding_rect.left, bounding_rect.top, client_rect.right,
client_rect.bottom, false);
}
init_ok = true;
}
}
if (loops % 10 == 0)
{
if (FindWindow(NULL, L"AssaultCube") == NULL)
{
SendMessage(hWnd, WM_CLOSE, NULL, NULL); // calls WindowProc() and sends the message to a window
}
}
loops++;
if (loops > 100) loops = 0;
Render();
}
}
/* This is my WindowProc() function: */
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}break;
case WM_LBUTTONDOWN:
{
PostMessage(gameWnd, message, wParam, lParam);
return 0;
}
case WM_MOUSEMOVE:
{
SendMessage(gameWnd, message, wParam, lParam);
return 0;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
You didn't post enough code to attempt to solve this problem, we don't see your call to CreateWindowEx().
Here is a working solution for a win32 overlay:
#include <iostream>
#include <windows.h>
using namespace std;
//global forward declerations
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void cleanUpObjects(HPEN pen);
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
RECT overlayWindowRect;
RECT gameWindowRect;
HWND gameWindowHandle;
gameWindowHandle = FindWindowA(0, "AssaultCube");
GetWindowRect(gameWindowHandle, &gameWindowRect);
WNDCLASSEX w;
w.cbSize = sizeof(WNDCLASSEX);
w.style = CS_HREDRAW | CS_VREDRAW;
w.lpfnWndProc = WndProc;
w.cbClsExtra = 0;
w.cbWndExtra = 0;
w.hInstance = hInstance;
w.hIcon = NULL;
w.hCursor = NULL;
w.hbrBackground = (HBRUSH)0;
w.lpszMenuName = NULL;
w.lpszClassName = "ClassName";
w.hIconSm = NULL;
if (!RegisterClassEx(&w))
{
MessageBox(NULL, "Could not Register Class", "Window Title", NULL);
return -1;
}
HWND hWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT, "ClassName", "Window",
WS_CAPTION,
gameWindowRect.left, //x
gameWindowRect.top, // y
gameWindowRect.right - gameWindowRect.left, // width
gameWindowRect.bottom - gameWindowRect.top, // height
NULL, NULL,
hInstance, NULL);
if (!hWnd)
{
MessageBox(NULL, "Call to create window failed", "Win32 Guided tour", NULL);
return -1;
}
else
{
GetWindowRect(hWnd, &overlayWindowRect);
}
// Remove Borders around window
SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) & ~(WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_BORDER));
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) & ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
// Make the Background Transparent
SetLayeredWindowAttributes(hWnd, RGB(255, 255, 255), 255, LWA_COLORKEY); // Replaces color white with transparancey
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
HDC myHDC = GetDC(hWnd);
// Drawing Stuff
HPEN myPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
HPEN originalPen;
originalPen = (HPEN)SelectObject(myHDC, myPen);
//main loop waits for messages
MSG msg;
while (true)
{
//peekmessage allows for program to do multiple things at once. faster than getmessage()
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
// if msg is quit, quit
if (msg.message == WM_QUIT)
break;
}
else
{
Rectangle(myHDC, 200, 200, 300, 400);
ZeroMemory(&gameWindowRect, sizeof(gameWindowRect)); //clear out the struct
GetWindowRect(gameWindowHandle, &gameWindowRect); // retrieves the games xy and height width and stores in gamewindowrect
if (gameWindowRect.right != overlayWindowRect.right) // checks if the x coordinates are the same
{
ZeroMemory(&gameWindowRect, sizeof(gameWindowRect)); // clear out struct once again
GetWindowRect(gameWindowHandle, &gameWindowRect); // get the dimensions of the game window again
MoveWindow(hWnd, gameWindowRect.left, gameWindowRect.top, gameWindowRect.right - gameWindowRect.left, gameWindowRect.bottom - gameWindowRect.top, TRUE);
// moves window to specific spot
}
}
Sleep(5);
}
cleanUpObjects(myPen);
cleanUpObjects(originalPen);
return msg.wParam;
}
LRESULT CALLBACK WndProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
PAINTSTRUCT ps;
HDC hdc;
switch (uMsg)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
DestroyWindow(hWnd);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
break;
}
return 0;
}
//cleans up objects
void cleanUpObjects(HPEN pen)
{
DeleteObject(pen);
}
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
I'm trying to get a Windows/DirectX Input in my little program. I have tried it now for 2 days, but it is not working as expected.
It only recognizes the WM_KEYDOWN message if I asked for the WM_QUIT message before that. I can't find the solution to this weird problem. You can see the code below.
while(m_Running)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if(msg.message == WM_QUIT)
{
MessageBox(NULL,"QUIT",NULL,MB_OK);
}
if(msg.message == WM_KEYDOWN)
{
MessageBox(NULL,"PRESS",NULL,MB_OK);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(Update() == false)
{
m_Running = false;
}
}
You should move your handlers into a win proc:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_QUIT:
// ....
break;
case WM_KEYDOWN:
// ....
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
And change your loop to:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Also, when you create your window class:
myclass.lpfnWndProc = WndProc;
Following on from the link I posted, I've downloaded Visual Studio Express 2013 and compiled run the following, tested it works.
This contains window and class creation, a message loop, and a windows procedure to process messages.
// Trim fat from windows
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// Windows Procedure Message Handler
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// Switch message, condition that is met will execute*/
switch (message)
{
case WM_CLOSE:
MessageBox(NULL, "QUIT", NULL, MB_OK);
PostQuitMessage(0);
break;
case WM_KEYDOWN:
MessageBox(NULL, "PRESS", NULL, MB_OK);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
// Main function
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX windowClass; //window class
HWND hwnd; //window handle
MSG msg; //message
/* Fill out the window class structure*/
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = WndProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = hInstance;
windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
windowClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = "MyClass";
windowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
/* Register window class*/
if (!RegisterClassEx(&windowClass))
{
return 0;
}
/* Class registerd, so now create window*/
hwnd = CreateWindowEx(NULL, //extended style
"MyClass", //class name
"A Real Win App", //app name
WS_OVERLAPPEDWINDOW | //window style
WS_VISIBLE |
WS_SYSMENU,
100, 100, //x/y coords
400, 400, //width,height
NULL, //handle to parent
NULL, //handle to menu
hInstance, //application instance
NULL); //no extra parameter's
/* Check if window creation failed*/
if (!hwnd)
return 0;
bool done = false; //initialize loop condition variable
/* main message loop*/
while (!done)
{
PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE);
if (msg.message == WM_QUIT) //check for a quit message
{
done = true;
}
else
{
// Translate and dispatch to event queue
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
Okay, i can understand if the function would return an error or throw an exception, but for some reason my call to DestroyWindow literally exits the program at that point. Like with the actual exit() function changing my program flow. The documentation mentions nothing like this and i have no means of figuring out what is going on since i get no error code. Has anyone ever encountered something like this?
I'm doing quite a bit more with this object than using winapi, so ignore the rest of it. What else could be wrong here?
SYNC_WinSystem.h
#ifndef SYNC_WINSYSTEM_H
#define SYNC_WINSYSTEM_H
#include "SYNC_ISystem.h"
#include <Windows.h>
#include <array>
#include "SYNC_Winput.h"
#include "SYNC_IRenderer.h"
#include "SYNC_D3D11Renderer.h"
#define FULL_SCREEN true
// SYNC_WinSystem
class SYNC_WinSystem : public SYNC_ISystem
{
public:
class WindowsContext;
SYNC_WinSystem();
virtual long Initialize(InitializeContext *);
virtual void Init_Loop();
virtual void Shutdown();
virtual long MakeDirectory(std::string);
virtual bool CreateSkin(std::string, std::string, SYNC::ISkin *&);
virtual ISound * CreateSound();
LRESULT CALLBACK MessageHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
private:
virtual long InitializeWindows();
virtual bool Frame();
virtual void ShutdownWindows();
private:
SYNC_Winput m_Input;
private:
std::shared_ptr<SYNC_IRenderer> m_Graphics;
HINSTANCE m_hinstance;
HWND m_hwnd;
int m_screenWidth;
int m_screenHeight;
std::string m_WindowName;
};
// SYNC_WinSystem::WindowsContext
class SYNC_WinSystem::WindowsContext : public SYNC_ISystem::InitializeContext
{
public:
WindowsContext();
std::string Type();
HINSTANCE m_hinstance;
std::string m_WindowName;
private:
const static std::string m_Identifier;
};
#endif
SYNC_WinSystem.cpp
#include "SYNC_WinSystem.h"
// SYNC_WinSystem definitions
SYNC_WinSystem * g_windows;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
SYNC_WinSystem::SYNC_WinSystem()
: m_Graphics(new SYNC_D3D11Renderer)
{
m_hinstance = nullptr;
m_hwnd = nullptr;
m_screenWidth = 0;
m_screenHeight = 0;
}
long SYNC_WinSystem::Initialize(InitializeContext * context)
{
long result = 0;
char errors[256];
WindowsContext * cContext;
if(context->Type() == "WindowsContext")
cContext = static_cast<WindowsContext *>(context);
else
return false;
m_hinstance = cContext->m_hinstance;
m_WindowName = cContext->m_WindowName;
g_windows = this;
result = InitializeWindows();
if(result)
{
sprintf_s(errors, "The Window could not initialize. Windows error code: %i", result);
MessageBox(NULL, errors, "Error!", MB_OK);
return result;
}
std::array<std::string, 3> folderNames=
{{
"Compiled_Models",
"Temp_Models",
"Materials"
}};
for(int i = 0; i < (int) folderNames.size(); i++)
{
result = MakeDirectory(folderNames[i]);
if( result && (result != ERROR_ALREADY_EXISTS))
{
sprintf_s(errors, "Error creating directory \" %s \" for system. Windows error code: %i", folderNames[i].c_str(), result);
MessageBox(NULL, errors, "Error!", MB_OK);
return result;
}
result = 0;
}
SYNC_D3D11Renderer::D3D11Context graphicsContext;
graphicsContext.fullscreen = true;
graphicsContext.hwnd = m_hwnd;
graphicsContext.screenDepth = 1000.0f;
graphicsContext.screenNear = 0.1f;
graphicsContext.screenWidth = m_screenWidth;
graphicsContext.screenHeight = m_screenHeight;
if(!m_Graphics->Initialize(&graphicsContext))
return false;
return result;
}
void SYNC_WinSystem::Init_Loop()
{
MSG msg;
bool done, result;
ZeroMemory(&msg, sizeof(MSG));
done = false;
while(!done)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(m_Input.IsKeyPressed(VK_ESCAPE))
{
done = true;
}
else
{
result = Frame();
if(!result)
{
done = true;
}
}
}
}
void SYNC_WinSystem::Shutdown()
{
ShutdownWindows();
}
long SYNC_WinSystem::MakeDirectory(std::string dirName)
{
DWORD result = 0;
long returnValue = 0;
dirName.insert(0, ".\\");
result = CreateDirectory(dirName.c_str(), NULL);
if(result == 0)
{
returnValue = GetLastError();
}
return returnValue;
}
bool SYNC_WinSystem::Frame()
{
if(!m_Graphics->Frame())
return false;
return true;
}
long SYNC_WinSystem::InitializeWindows()
{
DWORD result = 0;
WNDCLASSEX wc;
DEVMODE dmScreenSettings;
int posX, posY;
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = &WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = m_hinstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hIconSm = wc.hIcon;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = m_WindowName.c_str();
wc.cbSize = sizeof(WNDCLASSEX);
if(RegisterClassEx(&wc) == 0)
{
result = GetLastError();
return result;
}
m_screenWidth = GetSystemMetrics(SM_CXSCREEN);
m_screenHeight = GetSystemMetrics(SM_CYSCREEN);
if(FULL_SCREEN)
{
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = (unsigned long) m_screenWidth;
dmScreenSettings.dmPelsHeight = (unsigned long) m_screenHeight;
dmScreenSettings.dmBitsPerPel = 32;
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
posX = posY = 0;
m_hwnd = CreateWindowEx(WS_EX_APPWINDOW,
m_WindowName.c_str(),
m_WindowName.c_str(),
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP,
posX, posY, m_screenWidth, m_screenHeight,
NULL, NULL, m_hinstance, NULL);
}
else
{
m_screenWidth = 800;
m_screenHeight = 600;
posX = ((GetSystemMetrics(SM_CXSCREEN)/2) - (m_screenWidth/2));
posY = ((GetSystemMetrics(SM_CYSCREEN)/2) - (m_screenHeight/2));
m_hwnd = CreateWindowEx(WS_EX_APPWINDOW,
m_WindowName.c_str(),
m_WindowName.c_str(),
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP,
posX, posY, m_screenWidth, m_screenHeight,
NULL, NULL, m_hinstance, NULL);
}
if(!m_hwnd)
{
result = GetLastError();
return result;
}
ShowWindow(m_hwnd, SW_SHOW);
SetForegroundWindow(m_hwnd);
SetFocus(m_hwnd);
return result;
}
void SYNC_WinSystem::ShutdownWindows()
{
ShowCursor(true);
if(FULL_SCREEN)
{
ChangeDisplaySettings(NULL, 0);
}
if(DestroyWindow(m_hwnd) == 0)
{
char meh[256];
sprintf(meh, "error: %i" , GetLastError());
MessageBox(NULL, meh, "error!", MB_OK);
}
m_hwnd = NULL;
UnregisterClass(m_WindowName.c_str(), m_hinstance);
m_hinstance = NULL;
g_windows = NULL;
return;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
default:
{
return g_windows->MessageHandler(hwnd, msg, wparam, lparam);
}
}
}
LRESULT CALLBACK SYNC_WinSystem::MessageHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_KEYDOWN:
{
m_Input.KeyDown((unsigned int) wparam);
return 0;
}
case WM_KEYUP:
{
m_Input.KeyDown((unsigned int) wparam);
return 0;
}
default:
{
return DefWindowProc(hwnd, msg, wparam, lparam);
}
}
}
ISound * SYNC_WinSystem::CreateSound()
{
return nullptr;
}
bool SYNC_WinSystem::CreateSkin(std::string filename, std::string shaderName, SYNC::ISkin *& skin)
{
if(!m_Graphics->CreateSkin(filename, shaderName, skin))
return false;
return true;
}
// SYNC_WinSystem::WindowsContext definitions
const std::string SYNC_WinSystem::WindowsContext::m_Identifier = "WindowsContext";
SYNC_WinSystem::WindowsContext::WindowsContext()
{
}
std::string SYNC_WinSystem::WindowsContext::Type()
{
return m_Identifier;
}
quick explanation of how i do this. window handle and hinstance have private members in the object, and during the Initialize() and InitializeWindows() functions, the window class and window itself is created. The windows procedure is defined below as a global function, because you can't use a member function as a windows procedure. I dodge around this by making a global pointer (gasp) at the top, and assigning it to the this pointer of the system, which allows the procedure to call a member function procedure. Still only allows one instance of the system, but that's all i need :. Anywho, during shutdown, ShutdownWindows() is called, which then calls DestroyWindow. It is during this call, that my program simply ends, no error, no exception. MSVC++ express tells me it returns error code 3, but as far as windows error codes, that's just an ERROR_PATH_NOT_FIND which makes no sense in this context. Anyone have a clue?
main.cpp
#include <memory>
#include <Windows.h>
#include "Syncopate.h"
#include "SYNC_WinSystem.h"
using namespace std;
#include "SYNC_Winput.h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR lpCmdLine, INT nCmdShow)
{
//create a new instance of the engine
std::shared_ptr<SYNC_WinSystem> system( new SYNC_WinSystem);
// create an init context for this specific derivative of SYNC_ISystem
SYNC_WinSystem::WindowsContext context;
context.m_hinstance = hInstance;
context.m_WindowName = "Syncopate";
// Initialize the system object. if something goes wrong, return ERROR
if(system->Initialize(&context))
return 1;
SYNC::ISkin * model;
if(!system->CreateSkin("data.txt", "ColorShader", model))
return 1;
system->Init_Loop();
system->Shutdown();
return 0;
}
There's nothing in your message loop that will notice a WM_QUIT and abort. The "standard" practice for a message loop is to call GetMessage() until it fails, which indicates WM_QUIT, but you're calling PeekMessage() which has no handling for WM_QUIT at all. Your message loop only exits when done is true, which will only happen when escape is pressed or your Frame() call fails.
As you discovered, the solution was to redesign your loop to properly handle WM_QUIT messages and to not call DestroyWindow after the loop has already ended.
What seems to be going on is that you handle the WM_DESTROY message and explicitly call PostQuitMessage. I suspect somewhere that WM_QUIT message is being processed and causing an exit of some sort. I'll see if I can find any further information, but that's my understanding so far.
Rule of Thumb:
When you receive a WM_CLOSE message, call DestroyWindow. When you receive a WM_DESTROY message, call PostQuitMessage. Because you are using PeekMessage, you will get a message structure for a WM_QUIT message. When you find this message, you should end your loop.
IIRC exit code 3 is what you get by calling abort.
Anyway,
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
is very ungood (it terminates your message loop), and most likely the cause of your problems.
Just remove that.
Update: since the above recommendation didn't improve things, I suspect that you're calling DestroyWindow on an already destroyed window.
As a rule you should only call DestroyWindow in response to WM_CLOSE. For general windows that's the default handling. There is no indication in the presented code (as I'm writing this) of how your "shutdown" code is called, but due to the identical handling of WM_CLOSE and WM_DESTROY I suspect that it's a call placed after the message loop.
And in that case it is indeed likely that you're calling DestroyWindow on an already destroyed window.